作者:ryanmcdermott译者:前端小智来源:GitHub文章的分类也整理了很多我的文档和教程资料。欢迎来到星和完美。面试时可参考考点复习。我希望我们能在一起。设计模式的六大原则是:SingleResponsibilityPrinciple:单一职责原则OpenClosedPrinciple:开闭原则LiskovSubstitutionPrinciple:里氏代换原则LawofDemeter:Dimit'sLawInterfaceSegregationPrinciple:接口隔离原则DependenceInversionPrinciple:依赖性inversionprinciple结合了这六个原则的首字母(两个L算一个)是SOLID(solid,stable),意思是结合这六个原则的好处:建立稳定、灵活、稳健的设计。让我们来看看这六个设计原则中的每一个。单一职责原则(SRP)单一功能原则:单一功能原则是指一个对象应该只有一个单一功能的概念。也就是说,让一个类只做一种类型的职责。当这个类需要承担其他类型的职责时,需要分解这个类。在所有SOLID原则中,这是大多数开发人员认为理解最充分的原则。严格来说,这可能也是最常被违反的原则。单一职责原则可以看作是面向对象原则上低耦合高内聚的延伸,将职责定义为变化的原因,提高内聚以减少变化的原因。如果职责太多,其变化的原因就会更多,从而导致职责依赖和相互影响,从而极大地破坏其内聚性和耦合性。单一职责通常意味着单一的功能,所以不要为一个模块实现太多的功能点,以保证实体只有一个改变的原因。语法错误classUserSettings{constructor(user){this.user=user;}changeSettings(settings){if(this.verifyCredentials()){//...}}verifyCredentials(){//...}}写得不错classUserAuth{constructor(user){this.user=user;}verifyCredentials(){//...}}classUserSettings{constructor(user){this.user=user;this.auth=newUserAuth(用户);}changeSettings(settings){if(this.auth.verifyCredentials()){//...}}}开闭原则(OCP)软件实体应该是可扩展的,不可修改的。也就是说,它对扩展开放但对修改关闭。这个原理是众多面向对象编程原理中最抽象、最难理解的。通过添加代码而不是修改现有代码来扩展功能。如果客户端模块和服务模块按照相同的接口设计,客户端模块不需要关心服务模块的类型,服务模块可以很容易地扩展服务(代码)。OCP在不修改客户端模块的情况下支持备用服务。说白了:你不想改变吗?,那我就让你继承实现一个对象,用一个接口来抽象你的职责。你改变的越多,你继承和实现的子类就越多。不好的写法classAjaxAdapterextendsAdapter{constructor(){super();this.name="ajaxAdapter";}}classNodeAdapterextendsAdapter{constructor(){super();this.name="节点适配器";}}classHttpRequester{构造函数(适配器){this.adapter=adapter;}fetch(url){if(this.adapter.name==="ajaxAdapter"){returnmakeAjaxCall(url).then(response=>{//转换响应并返回});}elseif(this.adapter.name==="nodeAdapter"){returnmakeHttpCall(url).then(response=>{//转换响应并返回});}}}functionmakeAjaxCall(url){//请求并返回promise}functionmakeHttpCall(url){//请求并返回promise}好的写法classAjaxAdapterextendsAdapter{constructor(){super();this.name="ajaxAdapter";}request(url){//请求并返回promise}}classNodeAdapterextendsAdapter{constructor(){super();this.name="nodeAadapter";}request(url){//请求并返回promise}}classHttpRequester{constructor(adapter){this.adapter=adapter;}fetch(url){returnthis.adapter.request(url).then(response=>{//transformresponseandreturn});}}LiskovSubstitutionPrinciple(LSP)里氏替换原则:里氏替换原则认为“程序中的对象应该能够被它替换而不改变程序的正确性程序。“被子类代替”LSP的概念给了我们判断和设计类之间关系的依据:是否需要继承,如何设计继承关系。当一个子类的实例应该能够替换它的任何超类时,当它们是实例时,它们就具有is-A关系。继承之于OCP,相当于多态之于里氏代换原则。子类可以代替基类,客户使用基类。他们不需要知道派生类做了什么事情。这是行为责任的可替代原则。如果S是T的子类型,则S对象应该替换所有T对象而不改变任何抽象属性。客户端模块不应该关心服务模块是如何工作的。可以在不知道服务模块代码的情况下更换相同的接口模块。即接口或父类出现的地方,可以用实现该接口的类或子类代替。不好的写法classRectangle{constructor(){this.width=0;这个.height=0;}setColor(color){//...}render(area){//...}setWidth(width){this.width=width;}setHeight(height){this.height=height;}getArea(){返回this.width*this.height;}}classSquareextendsRectangle{setWidth(width){this.width=width;this.height=宽度;}setHeight(height){this.width=height;this.height=高度;}}functionrenderLargeRectangles(rectangles){rectangles.forEach(rectangle=>{rectangle.setWidth(4);rectangle.setHeight(5);constarea=rectangle.getArea();//错误:Square返回25。应该是20.rectangle.render(area);});}constrectangles=[newRectangle(),newRectangle(),newSquare()];renderLargeRectangles(rectangles);好的写法classShape{setColor(color){//...}render(area){//...}}类RectangleextendsShape{constructor(width,height){极好的();this.width=宽度;this.height=高度;}getArea(){返回this.width*this.height;}}classSquareextendsShape{constructor(length){super();长度;}getArea(){返回this.length*this.length;}}functionrenderLargeShapes(shapes){shapes.forEach(shape=>{constarea=shape.getArea();shape.render(area);});}constshapes=[newRectangle(4,5),newRectangle(4,5),新方块(5)];renderLargeShapes(形状);接口隔离原则(ISP)“客户端特定接口优于通用接口”的概念不能强迫用户依赖他们不使用的接口。换句话说,使用多个专用接口总是比使用多个专用接口更好一个单一的通用接口。这个原则起源于施乐公司,他们需要构建一个新的打印机系统,可以执行多项任务,例如装订一组打印件和传真。系统软件从底层创建并实现这些任务功能,但是软件功能的不断增加,使得软件本身越来越难以适应变化和维护,每一次变化,哪怕是最小的变化,都要有人重新编译和重新部署近一个小时,几乎不可能为了继续开发,他们聘请了Robert来帮助他们,他们首先设计了一个主类Job,可以用来实现几乎所有的任务功能。只要调用Job类的一个方法就可以实现一个功能,Job类变化很大。这是一个胖模型。如果客户端只需要一个打印功能,而其他与打印无关的方法和功能也与之耦合,ISP原则建议在客户端和Job类之间增加一个接口层。不同的功能有不同的接口。比如打印功能就是Print接口,然后把大的Job类分成继承不同接口的子类,这样就有了PrintJob类,ETC。错误的措辞classDOMTraverser{constructor(settings){this.settings=settings;这个。设置();}setup(){this.rootNode=this.settings.rootNode;这个.animationModule.setup();}traverse(){//...}}const$=newDOMTraverser({rootNode:document.getElementsByTagName("body"),animationModule(){}//大多数时候,我们不需要动画遍历.//...});写得很好classDOMTraverser{constructor(settings){this.settings=settings;this.options=settings.options;这个。设置();}setup(){this.rootNode=this.settings.rootNode;这个.setupOptions();}setupOptions(){if(this.options.animationModule){//...}}traverse(){//...}}const$=newDOMTraverser({rootNode:document.getElementsByTagName("body"),选项:{animationModule(){}}});DependencyInversionPrinciple(DIP)DependencyInversionPrinciple:依赖倒置原则认为一个方法应该遵循“依赖于抽象而非实例”的概念。依赖注入是这一原则的一种实现。依赖倒置原则(DIP)规定代码应该依赖抽象概念,而不是具体实现。高级模块不依赖于低级模块高级和低级模块都依赖于抽象;抽象不依赖于具体实现具体实现依赖于抽象抽象和接口能够分离模块之间的依赖关系类可能依赖于其他类来执行它们的工作。但是,它们不应该依赖于类的特定具体实现,而是依赖于类的抽象。这个原则真的很重要。社会分工和标准化是这一设计原则的体现。显然,这个概念会大大增加系统的灵活性。如果这些类只关心它们用来支持特定契约而不是特定类型的组件,那么可以快速轻松地修改这些低级服务的功能,而对系统其余部分的影响最小。不好的写法classInventoryRequester{constructor(){this.REQ_METHODS=["HTTP"];}requestItem(item){//...}}classInventoryTracker{constructor(items){this.items=items;//错误:我们已经创建了对特定请求实现的依赖。//我们应该只让requestItems依赖于请求方法:`request`this.requester=newInventoryRequester();}requestItems(){this.items.forEach(item=>{this.requester.requestItem(item);});}}constinventoryTracker=newInventoryTracker(["apples","bananas"]);inventoryTracker.requestItems();好的写法classInventoryTracker{constructor(items,requester){this.items=items;this.requester=请求者;}requestItems(){this.items.forEach(item=>{this.requester.requestItem(item);});}}classInventoryRequesterV1{constructor(){this.REQ_METHODS=["HTTP"];}requestItem(item){//...}}classInventoryRequesterV2{constructor(){this.REQ_METHODS=["WS"];}requestItem(item){//...}}constinventoryTracker=newInventoryTracker(["apples","bananas"],newInventoryRequesterV2());inventoryTracker.requestItems();原文:https://github.com/ryanmcderm...代码部署后可能存在的bug无法实时获知。之后为了解决这些bug,花费了大量的时间在日志调试上。给大家推荐一款好用的BUG监控工具Fundebug。交流文章每周更新。可以微信搜索“大千世界”阅读即时更新(比博文早一两篇)。这篇文章在GitHub上https://github.com/qq449245884/xiaozhi已经收录了,整理了很多我的文档。欢迎star和改进。面试可以参考考点。另外,关注公众号,后台会回复福利,可以看到福利,你懂的。
