PHP设计原则,参考《PHP核心技术与最佳实践》、《敏捷开发原则、模式与实践》一文PHP面向对象设计五原则、设计模式原则SOLID单一职责原则(单一职责原则(SingleResponsibilityPrinciple,SRP)定义/特性只有一个类变化的原因,一个类只承担一个责任(responsibility:变化的原因),避免相同的责任分散到不同的类,以及功能重复的问题。一个类职责太多,多个职责相互依赖,一个职责的转换会影响这个类完成其他职责的能力,当类变化的原因发生时,会受到意想不到的损害遵守SPR原理优点降低类之间的耦合度:当需求发生变化时,只修改一个类,从而隔离变化对类其他职责的影响提高类的复用性:按需引用,一个类负责一个职责,需求的变化只需要修改对应??的类或者增加一定的职责来降低类的复杂度:单一职责,功能分散降低一个类有多个职责类的复杂度代码示例classParseText{private$content;publicfunctiondecodeText(String$content){//TODO:解码内容}publicfunctionsaveText(){//TODO::save$this->content;}}/*思考问题:需要解析的文本类型有很多种——html、xml、json保存文本的方式也有很多种——redis、mysql、filecustomers当端只需要解析文本时,它必须引入saveText不需要的方法。这两个职责之间没有强依赖关系。如果职责要求有任何变化,则需要更改该类*//*符合SRP的设计职责拆分*/classDecoder{private$content;publicfunctiondecodeText(String$content){//TODO:解码内容}publicfunctiongetText(){return$this->content;}}classStore{公共函数ionsave($content){//TODE:save}}总结了很多软件设计所做的就是发现职责,合理分离职责之间的关系。如果应用程序的变化总是同时影响多个职责,则无需分离职责。接口隔离原则(InterfaceSegregationPrincipleISP)问题在设计应用程序时,类的接口是不内聚的。不同的客户端只包含一些集中的功能,但系统会强制客户端实现模块中的所有方法,并编写一些哑方法。这样的接口就变成了胖接口或者接口污染。这样的接口会给系统引入一些不当的行为,浪费资源,影响其他客户端程序,增强耦合等。ISP定义/特性不应该强制客户端依赖于他们不需要的类的方法/函数依赖于一个类应该建立在最小的接口上接口的实现类应该只呈现为单一职责原则遵循ISP原则优点分离的胖接口,每组接口提供特定的功能服务于特定的组客户端程序对一套接口不会/轻微影响其他接口/客户端程序,保证接口的纯洁性。解决方案是将胖接口分解为多个客户端特定的接口/多个接口。分离继承并使用delegation分离接口,两个对象参与处理同一个请求,接受请求的对象将请求委托给另一个对象处理代码示例/**公告接口*/interfaceEmployee{publicfunctionstartWork();publicfunctionendWork();}/**定义具体的客户端接口*/interfaceCoder{publicfunctionwriteCode();}interfaceUi{publicfunctiondesignPage();}classCoderClientimplementsEmployee,Coder{publicfunctionstartWork(){//TODO::开始工作时间}publicfunctionendWork(){//TODO::结束工作时间}publicfunctionwriteCode(){//TODO::开始写代码return'hellowworld';}}$c=newCoderClient();echo$c->writeCode();总结Fat类会导致它们的客户端程序之间出现异常且有害的耦合关系。通过将胖客户端分解为多个特定于客户端的接口,客户端与它们实际调用的方法紧密相关,从而不再依赖于它们不调用的方法。接口隔离应该小而频繁地进行。与ISP相比,SRP和ISP都解决了软件设计中的依赖性原则。SRP侧重于责任的划分。主要的约束类其实就是接口和方法,是程序中的细节和实现。ISP注重接口的隔离,约束的是接口。从更宏观的角度看,接口的抽象设计是开闭的(Open-ClosePrincipleOCP)。随着软件系统规模的不断扩大,系统维护和修改的复杂度不断提高。系统某一部分的变化通常会影响其他模块。正确运用OCP原则可以解决此类问题。定义/特征一个模块在扩展行为方面应该是开放的,在可变性方面应该是封闭的轻微影响现有系统/模块代码示例/**定义一个具有固定行为的抽象接口*/interfaceProcess{publicfunctionaction(String$content);}/**继承抽象接口并扩展不同的行为*/classWriteToCacheimplementsProcess{publicfunctionaction(String$content){return'将内容写入缓存:'.$content;}}classParseText{private$content;publicfunctiondecodeText($content){$this->content=$content;}publicfunctionaddAction(Process$process){if($processinstanceofProcess){return$process->action($this->content);}}}$p=newParseText();$p->decodeText('content');echo$p->addAction(newWriteToCache());总结一下,OCP的核心思想是抽象接口编程,抽象的比较稳定。让类依赖和固定抽象,让类通过面向对象的继承和多态来继承抽象,重写它的方法或固有行为。就是想新的扩展方法/功能来实现扩展。LiskovSubstitutionPrinciple(LSP)问题面向对象中大量的继承关系非常普遍和简单。继承规则是什么,继承层次的最佳规则是什么,如何优雅地设计继承关系,子类能够正确地重新创建基类中的一些方法是LSP原理要处理的问题。定义/属性子类必须能够替换它们的基类型:基类的任何出现都可以被子类替换,而客户端程序不会更改基类的行为或抛出异常和错误,但反之则不行。客户端程序应该只使用子类的抽象父类,这样可以针对LSP原理实现动态绑定(php多态)假设一个函数a,它的参数引用了一个基类b,c是b的派生类,如果把c的对象作为b类型传递给a,会导致a的错误行为,没有c就违反了LSP原则。/**基类*/计算机类{publicfunctionaction($a,$b){return$a+$b;}}/**子类审查了父类的方法,改变了action的行为*违反了LSP原则*/classClientextendsComputer{publicfunctionaction($a,$b){return$a-$b;}}functionrun(Computer$computer,$a,$b){return$computer->action($a,$b);}echorun((newClient()),3,5);总结LSP是一个OCP应用的最重要原则,正是因为子类可替换行是基类类型的扩展功能,无需修改。依赖倒置原则(DependInversionPrincipleDIP)问题在软件开发和设计中,总是存在创建一些依赖于低级模块的高层模块的倾向。低层模块发生变化时,会直接影响高层模块,从而迫使其发生变化。DIP原则描述了高层模块如何调用低层模块。定义/特性高层模块不应该依赖低层模块,两者都应该依赖抽象抽象不应该依赖细节,细节应该依赖抽象CodeexampleinterfaceArithmetic{//publicfunctionsub($a,$b);}classClient{publicfunctioncomputer(Arithmetic$arithmetic,$a,$b){return$arithmetic->add($a,$b);}}类加法实现算??术{publicfunctionadd($a,$b){return$a+$b;}}$c=newClient();echo$c->computer(newAddition(),2,3);/*client高层类依赖Arithmetic,Addition底层实现细节类实现Arithmetic接口,以两者的实现都依赖于抽象接口的DIP设计原则*/总结DIP的原则是每个高层模块为其所需要的服务定义一个接口声明,低层模块实现这个接口。每个高级类都通过这个抽象接口来消费服务。合理遵守面向对象软件开发中的设计原则可以带来更好的代码设计,减少不必要的错误,提高程序的可维护性、可扩展性和稳定性。SingleResponsibility(SRP)如何正确划分职责,单一类职责提高代码复用性,降低耦合InterfaceIsolation(OCP)合理划分接口功能,保证接口的专用性和纯度,减少依赖Liskovreplacement(LSP)合理使用类继承体系保证真正的继承关系不被破坏依赖倒置(DIP)抽象接口编程由于开放和封闭(OCP)面向对象编程的最终目标的抽象具体实现,类/模块/系统的的功能行为是可扩展的,内部可变性是封闭的
