// 抽象枪类 class AbstractGun { shoot(){ throw "Abstract methods cannot be called"; } } // 步枪 class Rifle extends AbstractGun { shoot(){ console.log("步枪射击..."); } } // 狙击枪 class AUG extends Rifle { zoomOut(){ console.log("通过放大镜观察"); } shoot(){ console.log("AUG射击..."); } } // 士兵 class Soldier { constructor(){ this.gun = null; } setGun(gun){ this.gun = gun; } killEnemy(){ if(!this.gun){ throw "需要给我一把枪"; return; } console.log("士兵开始射击..."); this.gun.shoot(); } } // 狙击手 class Snipper extends Soldier { killEnemy(aug){ if(!this.gun){ throw "需要给我一把枪"; return; } this.gun.zoomOut(); this.gun.shoot(); } } let soldier = new Soldier(); soldier.setGun(new Rifle()); soldier.killEnemy(); let snipper = new Snipper(); // 分配狙击枪 snipper.setGun(new AUG()); snipper.killEnemy(); snipper.setGun(new Rifle()); // snipper.killEnemy(); // this.gun.zoomOut is not a function
从上述代码中可以看出,子类和父类之间关系,子类方法一定是等于或大于父类的方法。子类能够出现的父类不一定能出现,但是父类出现的地方子类一定能够出现。
依赖倒置如果方法与方法之间或类与类之间,存在太多的依赖关系会导致代码可读性以及可维护性很差。依赖倒置原则能够很好的解决这些问题。
什么是依赖倒置依赖倒置原则:程序要依赖于抽象接口,不要依赖于具体实现。简单的说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块间的耦合。(节选自百度百科)
高层模块不应该依赖低层模块,两者都应该依赖其抽象
抽象不应该依赖细节
细节应该依赖抽象
依赖倒置好处通过依赖于接口,隔离了具体实现类
低一层的变动并不会导致高一层的变动
提高了代码的容错性、扩展性和易于维护
实例// 抽象枪类 class AbstractGun { shoot(){ throw "Abstract methods cannot be called"; } } // 步枪 class Rifle extends AbstractGun { shoot(){ console.log("步枪射击..."); } } // 狙击枪 class AUG extends AbstractGun { shoot(){ console.log("AUG射击..."); } }
从上面的代码可以看出,步枪与狙击枪的shoot全部都是依赖于AbstractGun抽象的枪类,上述编程满足了依赖倒置原则。
接口隔离 什么是接口隔离:客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上。(节选自百度百科)
接口隔离原则与单一职责原则的审视角度不相同。单一职责原则要求是类和接口的职责单一,注重的职责,这是业务逻辑上的划分。接口隔离原则要求接口的方法尽量少。
接口隔离好处避免接口污染
提高灵活性
提供定制服务
实现高内聚
实例function mix(...mixins) { class Mix {} for (let mixin of mixins) { copyProperties(Mix, mixin); copyProperties(Mix.prototype, mixin.prototype); } return Mix; } function copyProperties(target, source) { for (let key of Reflect.ownKeys(source)) { if ( key !== "constructor"&& key !== "prototype"&& key !== "name") { let desc = Object.getOwnPropertyDescriptor(source, key); Object.defineProperty(target, key, desc); } } } class Behavior { eat(){ throw "Abstract methods cannot be used"; } call(){ throw "Abstract methods cannot be used"; } } class Action { climbTree(){ throw "Abstract methods cannot be used"; } } class Dog extends Behavior{ eat(food){ console.log(`狗正在吃${food}`); } hungry(){ console.log("汪汪汪,我饿了") } } const CatMin = mix(Behavior,Action); class Cat extends CatMin{ eat(food){ console.log(`猫正在吃${food}`); } hungry(){ console.log("喵喵喵,我饿了") } climbTree(){ console.log("爬树很开心哦~") } } let dog = new Dog(); dog.eat("骨头"); dog.hungry(); let cat = new Cat(); cat.eat("鱼"); cat.hungry(); cat.climbTree();
大家一定要好好分析一下上面的代码,共有两个抽象类,分别对应不同的行为,Cat与Dog类拥有共同的行为,但是Cat又拥有其自己单独的行为,使用抽象(即接口)继承其方法,使用接口隔离使其完成各自的工作,各司其职。
迪米特法则