当前位置: 首页 > 后端技术 > PHP

设计模式六大原则(PHP)

时间:2023-03-29 21:40:02 PHP

设计模式的目的是为了更好的代码重用性、可读性、可靠性和可维护性。常用的六种设计模式是:单一职责原则(SRP)、里氏代换原则(LSP)、依赖倒置原则(DIP)、接口隔离原则(ISP)、迪米特定律(LOD)、开闭原则(OCP).
1.单一职责原则(SingleResponsibilityPrinciple)这个原则是针对类的,即一个类应该只负责一个职责。假设有一个部门类叫T,它下面有两个职责的方法叫P1,P2。如果在P1的职责发生变化时修改部门类T,可能会导致职责P2失效。举个栗子:我们用动物呼吸的场景来展示输出结果:但是,我们发现并不是所有的动物都呼吸空气,比如鱼呼吸水。根据SRP原则,我们应该将Animal类分为陆生动物和海洋生物,如下图:但是我们发现这样的修改代价很大,既要分解原来的类,又要修改client。直接修改Animal类违反了单一职责原则,但是代价很小。如下图:这种修改方法并没有改变原来的方法,而是给类增加了一个新的方法。但在方法层面,它符合单一职责原则。在实际编程中,只有逻辑足够简单,才能在代码层面违反单一职责原则;仅当类中的方法数量足够少时,才能在方法级别违反单一职责原则。遵循单一职责的优点:(1)降低类的复杂度,一个类只负责一个职责。(2)提高类的可读性和可维护性。(1)降低变更带来的风险。
2.里氏替换原则(LiskovSubstitutionPrinciple)该原则提出,如果对于每个T1类型的对象o1,都有一个T2类型的对象o2,使得T1定义的所有程序P在所有对象o1被o2替换时,则程序P的行为没有改变,则类型T2是类型T1的子类型。这句话的原句,不知道是翻译的问题还是什么的,显得晦涩难懂。其实可以简单理解为凡是引用基类的地方都必须能够透明地使用其子类的对象,尽量不要在子类中重写和重载父类的方法。继承作为面向对象的三大特性之一,在给程序带来极大方便的同时,也带来了弊端。例如,继承会给程序带来侵入性,降低程序的可移植性,增加对象之间的耦合度。如果一个类被其他类继承,那么在修改该类时,必须考虑所有的子类。而且父类修改后,子类相关的功能可能失效。举个栗子:运行结果:后来我们想做一个函数,将两个数相加并乘以100,这时候我们看到上面的类也有两个参数,不过只是一个减法而已。我们继承A,重写那个方法,不就是先求和再求积吗?代码如下:运行结果:结果发现,在业务逻辑代码不变的情况下,结果和预期的结果不一样。因为C类虽然继承了A类,但是重写了A类的subtract方法,导致原函数出错。在实际编码过程中,我们通常会重写父类的方法来完成新的功能,但这会让类继承体系的复用性特别差。这时候我们可以选择让A和C共同继承一个比较流行的基类,然后实现他的方法,去掉A和C之间的继承关系,取而代之的是依赖、聚合、组合等关系。举个例子:这样既可以保持原有的业务关系,又可以实现更多的功能。3.依赖倒置原则(DependenceInversionPrinciple)依赖倒置规定:高层模块不应该依赖低层模块,两者都应该依赖于它们的抽象;抽象不应该依赖于细节,细节应该依赖于抽象。因为比起细节的多变性,抽象的东西要稳定得多。基于抽象的架构比基于细节的架构稳定得多。DependencyInversion的中心思想是面向接口编程。上层模块不应该依赖下层模块,而应该依赖接口。从而使下层模块依赖于上层接口,降低了耦合度,提高了系统的灵活性。这六项原则是最想象的、最抽象的,也是最难理解的。举个例子说明一下:但是如果我们看报刊杂志,就会发现那本书是不适用的。我们引入了一个抽象接口IReader,它代表阅读材料。让Mother类和IReader接口有依赖关系,Book和Newspaper都属于阅读类。让他们各自实现IReader接口,这样就符合了高层不依赖底层,依赖接口的依赖倒置原则。修改后的代码如下:使用依赖倒置原理后,我们会发现它给我们带来了很大的方便。例如,一开始,Mother类与Book类耦合。如果要修改读物,必须新建一个读物类,然后修改Mother类。传入的类名很麻烦。修改后Mother类直接依赖IReader接口,从而与Book解耦,每次只需要新建一个reader类实现IReader接口即可调用依赖转移。方法有接口传递和构造方法传递三种。和setter方法传递。1.接口传递2.构造方法传递(常用)3.Setter方法传递在实际编程中尽量注意以下三点:1.底层模块必须有抽象类或接口类,或两者都有2.变量的声明类型尽量是抽象类或接口(这里指的是传入的变量所代表的类)3.遵循里氏代换原则。控制反转(IOC)和依赖注入(DI)也是基于这个原理。所有的类实例化都放在一个容器中,调用都从容器中获取,减少了高层类对低层类的依赖。学过IOC和DI的可以留个邮箱,我发一些理解的例子。4.接口隔离原则(InterfaceSegregationPrinciples)一个类不应该依赖它不需要的接口;一个类对另一个类的依赖应该基于最小的接口。例如,类A通过接口E依赖于类B,类C通过接口E依赖于类D。如果接口E不是类A和类C的最小接口,那么类B和类D必须实现它们做的方法不需要。这时候我们把臃肿的接口拆分成几个独立的接口,A类和C类分别与自己需要的接口建立依赖关系。这就是接口隔离原则。举个例子:可以看出,接口中出现的方法,不管对依赖它的类有没有影响,实现类都必须实现这些方法。这时候我们拆分接口来实现接口隔离的原则。举个例子:看到这里,你可能会觉得接口隔离原则和单一职责原则很像。其实不是,1.单一职责原则侧重于本类的职责,而接口隔离原则侧重于接口依赖的隔离。2.单一职责约束类,其次是方法,针对的是程序中的实现和细节。接口隔离原则对接口、抽象和程序框架的整体构造进行了约束。5.Demeter法则(也称为最少知识原则)一个对象应该对其他对象保持最少的知识。类与类之间的关系越紧密,耦合度就越大。Demeter原则也叫最少知识原则,即一个类对它所依赖的类了解得越少越好。也就是说不管依赖类多么复杂,尽量把逻辑封装在类内部。对外仅提供公共方法,不对外公开任何信息。得墨忒耳原则还有一个更简单的定义:只与直系亲友交流。什么是直接朋友:每个对象都与其他对象存在耦合关系。只要两个对象之间存在耦合关系,我们就称这两个对象为友元关系。耦合的方式有很多种,如依赖、关联、组合、聚合等。其中,我们把出现在成员变量、方法参数、方法返回值中的类称为直接友元,而出现的类在局部变量中不是直接的朋友。也就是说,不熟悉的类最好不要以局部变量的形式出现在类内部。例如:这个设计的问题是在CompanyManager中,SubCompanyManager类不是它的直接友元。根据迪米特定律,应该避免类中这种间接的友谊耦合。修改迪米特定律如下,降低类之间的耦合度,使每个类减少不必要的依赖;但过度使用迪米特定律会产生大量的中间类和传输类,导致系统复杂度变大。因此,在采用迪米特定律时,需要反复权衡,既要做到结构清晰,又要做到高内聚、低耦合。开闭原则(OpenClosePrinciple)一个软件实体,如类、模块、函数等,应该对扩展开放,对修改关闭。用抽象构建框架,用实现扩展细节。当软件需要变更时,尽量通过扩展软件实体的行为来实现变更,而不是修改现有代码。当我们遵循前面介绍的5个原则,而使用23种设计模式的目的就是遵循开闭原则。简单的理解就是在构建框架的时候要保持足够的可扩展性,通过扩展来修改代码,而不是直接修改代码。