这几天去面试,面试官把简单的问完了,然后问到了设计模式(我以为是MVC之类的),被问懵了,一直都没有接触过这部分的内容,今天就来学习一下吧.
1、了解组合和继承吗,为什么要遵循多用组合少用继承的策略呢?
-
组合和继承是各有优点的.
-
举个例子,汽车不能闯红绿灯,这是所有汽车都遵循的规则.那么对这种通用规则,使用继承就显得更方便了,也是很容易被理解的.那么继承就是优于组合的.
-
class Car { constructor() { this.car = '汽车' } fllowingRules() { console.log('汽车不能闯红绿灯'); } } -
那为啥要多用组合少用继承呢?
-
再举个例子,汽车有能源车和汽车,那么能源车和标准汽车分别是通过充电和加油来进行启动的,这时候,能源车 = 汽车的规则 + 充电, 标准汽车 = 汽车的规则 + 加油,这种为汽车赋能组合的方式,比起继承而言,更加合理.因为如果你在最早就为汽车定义了加油的方法,那么当子类在继承车这个父类时,也就表明了车 is a 汽油启动的, 但是对于新能源车而言却不是这样的.
-
class Car { constructor(name) { this.carName = name } fllowingRules() { console.log(this.carName + '不能闯红绿灯'); } } class engryCar extends Car { constructor(name) { super(name); } charge() { console.log('这是能源车,是充电启动的'); } } class commonCar extends Car { constructor(name) { super(name); } gasonline() { console.log('这是汽车,是汽油启动的'); } } -
而需要多用组合的原因: 是业务逻辑的「不确定性」,就像车出来时,没人知道车还能充电,当使用继承时,可能是对于部分子类不适用的,这时就需要去patch了,随着复杂度的提高,可能会发现需要不断patch,从而降低清晰度和可读性,这时组合的优势
模块化和可扩展性就体现出来了,你可以将部分不依赖于当前类存在的功能模块化,然后进行组合使用,这种优势会在业务复杂度提高时被体现出来. -
总结: 我觉得其实对于继承和组合没有多用和少用之说,对于该用继承的或者说
is a关系的就选择使用继承,对于has a就使用组合.
2、装饰器?
看了这么多文章居然都没看到有说到过装饰器的这个概念😣
- 装饰器是一种不修改内容本身,用于增强目标类的方式.做到了面向切面编程,用于实现调用者和被调用者解耦的方式
- 使用装饰器可以很方便的做到函数组合.
1、类装饰符
-
const defineType = (type) => (target) => { target.prototype._type = type; } const defineCharge = (chargeType) => (target) => { target.prototype.chargeType = chargeType; } @defineCharge('充电') @defineType('engery') class Car { name: string; _type: string; chargeType: string; constructor(name: string) { this.name = name; } }
属性装饰符
-
const readonly = (value) => (target,val) => { console.log(arguments); Object.defineProperty(target,val,{ value, writable: false }); } class Person { @readonly('person') name; } const person = new Person(); person.name = 'tom'; console.log(person.name); -
在ts中可以通过传入的实例本身和属性来对数据做defineProperty的操作.
-
还有方法装饰器和参数装饰器,这里就不细讲了.
-
这一块的内容资料是真的难找,挺多文章和自己打出来的有出入,主要是函数式编程是主流了.而且这一块还没有被标准化.
-
但是思路还是值得学习的,最后贴一下设计模式的六大原则吧.
子类可以扩展父类的功能,但是子类不允许修改父类上的功能被称为「里氏代换原则」
对于扩展开放,对于修改关闭的原则称为「开闭原则」(有点前向兼容的味道)
高层不应该依赖低层,两者应该依赖抽象的原则被称为「依赖倒置原则」(简单来说就是对于有相同表现形式的内容,应当以接口为标准去实现,这样就解耦了对象依赖,且增加了可读性和扩展性)
不会造成多余一个导致类变更的原因称为「单一职责原则」(简单点说就是一个类负责一个功能)
一个类对另一个类的依赖应该建立在最小的接口上称为「接口隔离原则」(接口应该是细化的、组合使用的,这样才不会造成有交集的类之间,不会有多余的、不合理的属性(接口污染))
一个对象应当对其他对象有尽可能少的了解,不和陌生人说话称为「迪米特法则」(这样做的目的是,减少由其他类对自身的影响,降低耦合) 最后说一下在JS其实并没有实现完美的面向对象,如果继承、多态、封装是三分的话,它顶天只占了继承(原型链)和多态(动态性),而对于
包裹对象内容,并给予外部不同的访问权限的多态是没有的「非要说闭包的话那就没办法了😂 」,当然这些都在TS被解决了,但TS也只是以静态检测的方式提供用户信息.