面向对象编程有自己的特点和原则。如果对面向对象有一定的了解,面向对象的三大特性,封装,继承,多态。三个概念不好理解,请参考面向对象(JavaScript)单一职责的三个基本特性如果我们写一个程序,一个类或者一个方法包含了太多的方法,为了代码的可读性,无非是一场灾难,对我们来说。所以为了解决这个问题,SingleResponsibility应运而生。什么是单一职责单一职责:又称单一功能原则,面向对象(SOLID)五项基本原则之一。它指出一个类应该只有一个改变的理由。(摘自百度百科)按照上面的说法,对于一个类来说,它的变化应该只有一个原因。换句话说,一个类应该只有一个功能,并且只做与其相关的事情。在类设计过程中,应按职责进行设计,保持相互正交,互不干扰。单一职责的好处降低了类的复杂性,明确定义了任何职责。可读性提高了,复杂度降低了,当然可读性也提高了。提高了可维护性,提高了可读性。当然更容易维护为了降低变更带来的风险,变更是必不可少的。如果接口的单一职责做得好,一个接口的修改只会影响对应的实现类,对其他接口没有影响。这会影响系统的可扩展性和维护性。性非常有帮助。示例classShoppinCar{constructor(){this.goods=[];}addGoods(good){this.goods=[good];}getGoodsList(){returnthis.goods;}}classSettlement{constructor(){this.result=0;}calculatePrice(list,key){letallPrice=0;list.forEach((el)=>{allPrice+=el[key];})this.result=allPrice;}getAllPrice(){returnthis.result;}}根据上述代码中,ShoppinCar类中有两个方法addGoods和getGoodsList,分别是添加商品和获取商品列表。Settlement类中有两个方法calculatePrice和getAllPrice,分别计算价格和获取总价。ShoppinCar和Settlement都各司其职。添加商品和计算价格,虽然在业务上是相互依赖的,在代码中使用了两个类,但各司其职。任何一个类的改变都不会改变另一个类。开闭原则就是暴露一个类中的一个方法。如果改变这种方式,将会带来很大的后果,可能导致依赖这种方式的其他不需要改变的业务大规模瘫痪。为了解决这个问题,可以单独写一个方法,如果这个方法和这个类的其他方法相互依赖的话。解决方法:将依赖的代码复制到一个新的类中。在新类中引用旧类中的方法。这两种方法都不是最佳解决方案。第一种方式会导致大量重复代码,第二种方式会造成类之间相互依赖。什么是开闭原则?TheOpen-ClosedPrinciple:“软件中的对象(类、模块、函数等)改变其行为而不改变源代码。(摘自百度百科)开闭原则是对扩展开放,对修改关闭,不代表不做修改,底层模块的改动必须和高层模块耦合,否则就是一个孤立无意义的代码片段,开闭原则是最基本的原则。六大原则是开闭原则的具体表现形式,是指导设计的工具和方法,开闭原则是精神领袖。开闭原则的好处单元测试开闭原则可以提高复用性开闭原则可以提高可维护性面向对象开发的要求示例classDrag{down(){//...}move(){//...//拖放你可以拖放不受任何限制随意放下}up(){//...}}classLimitDragextendsDrag{move(){//...//重写这个方法限制拖动}}重写LimitDrag中的move方法,如果修改,可以满足两个要求,一个是限制拖拽,一个是无限拖拽,换了一个还可以正常运行。LiskovReplacement每个开发者在使用别人的组件时,只需要知道组件对外暴露的接口,也就是它所有行为的集合。至于内部实现是如何实现的,无从知道,也没有必要知道。因此,对于用户来说,只能通过界面来实现自己的期望。如果组件接口提供的行为不符合用户的期望,就会发生错误。Liskov替换原则是为了避免在设计过程中派生类和基类之间出现不一致的行为。什么是LiskovReplacementLiskovReplacementPrinciple:OCP作为OO的高层原则,提倡利用“抽象”和“多态”将设计中的静态结构变为动态结构,以保持设计的封闭性。“抽象”是语言提供的一种特性。“多态”是通过继承语义来实现的。(摘自百度百科)Liskov替换的好处代码共享,减少创建类的工作量,每个子类都有父类的方法和属性,提高代码复用性子类可以和父类相似,但又和父类不同.为了提高代码的可扩展性,只需实现父类的方法即可。很多开源框架的扩展接口都是通过继承父类来完成的。提高产品或项目的开放性//抽象枪类classAbstractGun{shoot(){throw"Abstractmethodscannotbecalled";}}//Rifle类RifleextendsAbstractGun{shoot(){console.log("步枪射击...");}}//狙击步枪classAUGextendsRifle{zoomOut(){console.log("通过放大镜看");}shoot(){console.log("AUG射击...");}}//士兵类Soldier{constructor(){this.gun=null;}setGun(gun){this.gun=gun;}killEnemy(){if(!this.gun){throw"需要给我一把枪";return;}控制台。log("SoldierStartshooting...");this.gun.shoot();}}//SniperclassSnipperextendsSoldier{killEnemy(aug){if(!this.gun){throw"需要给我一把枪";return;}this.gun.zoomOut();this.gun.shoot();}}letsoldier=newSoldier();soldier.setGun(newRifle());soldier.killEnemy();letsnipper=newSnipper();//分配一把狙击枪snipper.setGun(newAUG());snipper.killEnemy();snipper.setGun(newRifle());//snipper.killEnemy();//this.gun.zoomOutisnotfunction从上面可以看出代码、子类和关系父类之间的传递,子类的方法必须等于或大于父类的方法。子类能出现的父类不一定会出现,但子类一定会出现在父类出现的地方。依赖倒置如果方法之间或类之间的依赖过多,代码的可读性和可维护性就会变差。依赖倒置原则可以很好的解决这些问题。什么是依赖倒置DependencyInversionPrinciple:程序依赖抽象接口,不依赖具体实现。简单的说,就是要求对抽象进行编程,而不是对实现进行编程,降低了客户端与实现模块之间的耦合度。(摘自百度百科)高层模块不应该依赖低层模块,两者都应该依赖于它们的抽象。抽象不应依赖于细节。细节应该依赖于抽象。依赖倒置的好处通过依赖接口,它隔离了具体的实现类。会导致更高层次的变化,提高了代码的容错性、可扩展性和易维护性Instance//AbstractgunclassclassAbstractGun{shoot(){throw"Abstractmethodscannotbecalled";}}//RifleclassRifleextendsAbstractGun{shoot(){console.log("Rifleshooting...");}}//狙击枪classAUGextendsAbstractGun{shoot(){console.log("AUGshooting...");}}从上面的代码中,我们可以看到步枪和狙击枪的射击都是依赖AbstractGun抽象的枪类,上面的编程满足了依赖倒置的原则。接口隔离什么是接口隔离接口隔离:客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上。(摘自百度百科)接口隔离原则和单一职责原则有不同的观点。单一职责原则要求类和接口的职责单一,并以职责为中心,即业务逻辑的划分。接口隔离原则要求接口的方法越少越好。接口隔离的好处避免接口污染提高灵活性提供定制化服务实现高内聚;}returnMix;}functioncopyProperties(target,source){for(letkeyofReflect.ownKeys(source)){if(key!=="constructor"&&key!=="prototype"&&key!=="name"){letdesc=Object.getOwnPropertyDescriptor(source,key);Object.defineProperty(target,key,desc);}}}classBehavior{eat(){throw"Abstractmethodscannotbeused";}call(){throw"Abstractmethodscannotbeused";}}classAction{climbTree(){throw"Abstractmethodscannotbeused";}}classDogextendsBehavior{eat(food){console.log(`狗正在吃${food}`);}hungry(){console.log("哇,我饿了")}}constCatMin=mix(行为,动作);classCattextendsCatMin{eat(food){console.log(`猫正在吃${food}`);}hungry(){console.log("喵喵,我饿了")}climbTree(){console.log("爬树很好玩~")}}letdog=newDog();dog.eat("bones");dog.hungry();letcat=newCat();cat.eat("fish");cat.hungry();cat.climbTree();大家一定要仔细分析上面的代码。两个抽象类对应不同的行为,Cat和Dog类有共同的行为,但是Cat有自己独立的行为,它用抽象(也就是接口)来继承它的方法,用接口来隔离它们来完成各自的任务Dimiter'sLaw法则:最少知识原则(LeastKnowledgePrinciple缩写为LKP),意思是一个对象应该尽可能少地了解其他对象,并且不与陌生人交谈。英文缩写为:LoD。(摘自百度百科)的概念迪米特定律是类之间的解耦和弱耦合,弱耦合之后才能提高类的复用率,一个类应该对其他对象保持最少的了解。,越好。因为类之间的关系越近,耦合度就越大。当一个类发生变化时,对另一个类的影响就越大。Demeter定律的好处降低对象实例之间的耦合度)}closeScreen(){console.log("closeScreen")}closePower(){console.log("closePower")}close(){this.saveCurrentTask();this.closeService();this.closeScreen();this.closePower();}}classIContainer{sendCloseCommand(){throw"不能使用抽象方法";}}classContainerextendsIContainer{constructor(){super()this.system=newSystem();}sendCloseCommand(){this.system.close();}}classPersonextendsIContainer{constructor(){super();this.container=newContainer();}clickCloseButton(){this.container.sendCloseCommand();}}letperson=newPerson();person.clickCloseButton();上面代码中,以Container作为媒介,调用类并不知道其内部是如何实现的。当用户触发按钮时,Container将消息通知计算机,计算机执行相应的命令。Combination/AggregationReusePrinciple聚合(Aggregation)代表一种弱的“拥有”关系,也就是说一个A对象可以包含一个B对象但是B对象不是A对象的一部分。组合是一种强“拥有”关系,体现了严格的部分对整体的关系,部分与整体具有相同的生命周期。组合/聚合:通过获取其他对象的引用在运行时动态定义,即将其他对象的属性保存在一个对象中。这个方法要求对象有定义好的接口,而且这个接口不经常变化,只能通过接口访问对象,这样我们就不会破坏封装,所以只要类型一致,一个对象可以在运行时被另一个对象替换。使用对象优先的组合/聚合将帮助您保持每个类的封装并专注于单个任务,因此类和类继承层次结构保持较小并且不太可能成长为难以管理的庞然大物。Composition/AggregationReusePrinciple的好处新的实现更容易,因为超类的大部分功能可以通过继承关系自动进入子类;修改或扩展继承的实现更容易。实例functionmix(...mixins){classMix{}for(letmixinofmixins){copyProperties(Mix,mixin);copyProperties(Mix.prototype,mixin.prototype);}returnMix;}functioncopyProperties(target,source){for(letkeyofReflect.ownKeys(source)){if(key!=="constructor"&&key!=="prototype"&&key!=="name"){letdesc=Object.getOwnPropertyDescriptor(source,key);Object.defineProperty(target,key,desc);}}}classSavings{saveMoney(){console.log("存钱");}withdrawMoney(){console.log("取钱");}}classCredit{overdraft(){console.log("透支")}}constCarMin=mix(Savings,Credit);classUserCarextendsCarMin{constructor(num,carUserName){super();console.log()this.carNum=num;this.carUserName=carUserName;}getCarNum(){returnthis.carNum;}getCarUserName(){returnthis.carUserName;}}letmyCar=newUserCar(123456798,"Aaron");console.log(myCar.getCarNum());console.log(myCar.getCarUserName());myCar.saveMoney();myCar.withdrawMoney();myCar.overd筏();总结一下,这些原则在设计模式中得到了充分的体现。设计模式就是实现这些原则,从而实现代码重用,增强系统的可扩展性。因此,设计模式被很多人奉为经典。我们可以通过好好研究设计模式来慢慢理解这些设计原则。
