设计模式六大原则(PHP)
设计模式的目的是为了更好的代码重用性,可读性,可靠性和可维护性。常用的六大设计模式有:单一职责原则(SRP),里氏替换原则(LSP),依赖倒转原则(DIP),接口隔离原则(ISP),迪米特法则(LOD),开闭原则(OCP)。
1.单一职责原则(Single Responsibility Principle)
该原则是针对类来说的,即一个类应该只负责一项职责。假设有一个部门的类叫做T,他的下面有两个职责的方法叫做P1,P2。假如P1的职责发生改变时去修改这个部门类T,那么有可能造成职责P2发生故障。
举个栗子:
我们用动物呼吸的场景来表现一下
输出结果:
但是呢,我们发现并不是所有的动物都是呼吸空气的,比如说鱼它是呼吸水的。根据SRP原则,我们应该将Animal类分为陆地动物和海洋生物,如下所示:
但是我们发现这样修改花销很大,既要将原来的类分解,又要修改客户端。而直接修改Animal类则违背了单一职责原则,但花销很小 如下所示:
这种修改方式没有改变原来的方法,而是在类中新加了一个方法,这样虽然违反了单一职责原则,但是在方法级别上却是符合单一职责原则的。在实际的编程中,只有逻辑足够简单,才可能在代码级违反单一职责原则;只有类中的方法数量足够少,才可以在方法级别上违反单一职责原则。
遵循单一职责的优点:
(1)降低类的复杂度,一个类只负责一项职责。
(2)提高类的可读性,可维护性。
(1)降低变更引起的风险。
2.里氏替换原则(Liskov Substitution Principle)
该原则提出,如果对每个类型为T1的对象o1,都有类型为T2的对象o2,使得以T1定义的所有程序P在所有的对象o1都代换成o2时,程序P的行为没有发生变化,那么类型T2是类型T1的子类型。这话原句,不知道是翻译的锅还是咋地,看起来就晦涩难懂。其实可以简单地理解为所有引用基类的地方必须能够透明的使用其子类的对象,在子类中尽量不要重写和重载父类的方法。
继承作为面向对象的三大特性之一,在给程序带来巨大便利的同时,也带来了弊端。比如继承会给程序带来可入侵行,程序的可移植性降低,增加了对象间的耦合性。如果一个类被其他类所继承,那么这个类在被修改的时候,必须考虑到所有的子类。并且父类在修改后,所以涉及到子类的功能都有可能发生故障。
举个栗子:
运行结果:
后来呢,我们想做个功能,将两个数相加并且乘以100.这个时候我们看到上面那个类也是两个参数,只不过是相减。我们继承一下A重写下那个方法不就完成求和再求积吗?代码如下:
运行结果:
结果我们发现,在业务逻辑代码没变的情况下结果居然跟预期的结果不一样了。因为C类虽然继承了A类,但是它重写了A类的subtract方法,造成了原有功能的错误。在实际的编码过程中我们通常会重写父类的方法来完成新的功能,但是这样会使得类的继承体系复用性特别差。这个时候我们可以选择让A和C共同继承一个更通俗的基类,然后实现他的方法,去掉A和C的继承关系,采用依赖、聚合、组合等关系代替。举个栗子:
这样我们既可以保持原有的业务关系,又可以实现更多的功能。
3.依赖倒转原则(Dependence Inversion Principle)
依赖倒置规定:高层模块不应该依赖于低层模块,二者都应该依赖其抽象;抽象不应该依赖于细节,细节应该依赖于抽象。因为相对于细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建的架构要比以细节为基础的架构要稳定的多。依赖倒置的中心思想是面向接口编程。上层模块不应该依赖于下层模块,应该依赖于接口。从而使得下层模块依赖于上层的接口,降低耦合度,提高系统的弹性。这六大原则是最虚,最抽象的,很难理解。举个栗子说明:
但是如果我们读的是报纸,杂志呢,发现book并不适用了。我们引入一个抽象的接口IReader,代表读物。让Mother类与接口IReader发生依赖关系,而Book和Newspaper都属于读物的范畴,让他们各自都去实现IReader接口,这样就符合高层不应该依赖低层,应该依赖于接口的依赖倒置原则,修改后代码如下:
用了依赖倒置原则之后会发现带给我们极大的便利,比如例子,一开始Mother类与Book类耦合,如果要修改读物的话,必须要创建一个新的读物类,然后修改Mother类中传入的类名,非常的麻烦。而修改后Mother类则直接依赖了IReader接口,这样与Book解耦,每次只需要创建一个新的读物类实现IReader接口即可调用
依赖关系的传递有三种办法,分别是接口传递、构造方法传递以及setter方法传递。
1.接口传递
2.构造方法传入(常用)
3.setter方法传递
在实际的编程中尽量注意以下三点:1.低层模块尽量都要有抽象类或者接口类,或者两者都有2.变量的声明类型尽量是抽象类或者接口(这里是指传入的那个变量代表的类)3.遵循里氏替换原则。控制反转(IOC)和依赖注入(DI)也是基于此原则,把所有类的实例化放在了一个容器中,从容器中获取调用,降低了高层类对低层类的依赖。有学习IOC和DI的,可以留个邮箱,我会把一些理解的例子发一下。
4.接口隔离原则(InterfaceSegregation Principles)
一个类不应该依赖他不需要的接口;一个类对另一个类的依赖应该建立在最小接口上。比如类A通过接口E依赖类B,类C通过接口E依赖类D,如果接口E对于类A和类C来说不是最小接口的话,则类B和类D必须去实现他们不需要的方法。这个时候我们将臃肿的接口拆分成独立的几个接口,类A和类C分别与他们需要的接口建立依赖关系。这就是接口隔离原则。举个栗子: