浅述PHP设计模式(2)
4、SOLID原则浅述
SRP:The Single Responsibility Principle 单一责任原则
单一责任原则,要求,一个函数只完成一个功能,一个类,只实现一类具体的对象。
这样说仍很抽象。但更具体一些,对于一个函数,应当是一个输入与输出单元。即有明确的输入数据,算法,输出数据的结构。
对于一个类,在核心代码层面,它应当是一个明确的算法实体,或者是功能实体。如果是在应用的具体层面,应当是与实际对象相对应的实体,或值对象。
这实际上是与具体的需求也是分不开的。
比如,数据库操作,如果只写一个类,是否符合SRP?假如核心代码面对的应用不会操作另一种数据库。那它是符合SRP的。
但是,如果应用大了,涉及到与其它应用集成,就会产生要访问其它类型的数据的问题。这时,就需要将统一接口类与驱动分开。数据库核心给应用提供统一接口,但底层针对不同数据库要有不同的驱动类。
再进一步,当访问量加大,则需要多数据库支持,因而,就需要将链接管理独立出来,将表访问管理独立出来,实现多数据库与多表调度访问。
所以,SRP是由设计者把控的。好的设计者,应当清楚潜在需求,从而使类,函数均符合SRP。
OCP:The Open Closed Principle 开放封闭原则
开放闭合原则是指,对扩展开放,对修改关闭。这里的扩展,不单指简单的继承一个类,而有可能通过增加类的方式扩展。
这是针对一个类的组合而言。任意一个类的组合实现之前,我们先要考虑的是,其中哪一些部分虽然属于与具体数据无关,但仍与具体的实际情况的变化有关。这就是说,这一部分中,仍要将其分类核心与具体两部分。所谓具体的,则就是有可能会增加的,有可能会变化的部分。而这些具体的部分,就需要分析它是什么样的调用模式,是具体调用核心,那具体情况下,只要继承此类,对其扩展即可。如果是核心调用具体,则我们要用面向对象的抽象类,接口将其定义出来。这样的代码就能从容应对变化。
比如前述的数据库操作,统一接口是核心代码中的核心,而针对不同数据库的驱动,则是具体的问题。因而,将其独立到一个类中,就可以实现方便的扩展。
LSP: The Liskov Substitution Principle 里氏替换原则
里氏替换原则是指:当一个子类的实例应该能够替换任何其超类的实例时,它们之间才具有is-A关系。
这里所讲的仍是变与不变的关系。哪些东西维持不变才能符合LSP呢?看一下不符合LSP的情况更易于了解:
当子类中不存在父类规定要实现的方法时,就不能替的父类的实例。同样,当子类中方法返回的数据类型与父类同一方法返回的数据类型不一致。等等等等。
由此,这就需要维持接口的一致性,否则,我们就会给调用方带来很多的if else, switch case,而这些都是不必要的。
很显然,这也会造成程序逻辑的混乱,难以维护与扩展。
ISP: The Interface Segregation Principle 接口分离原则
接口分离原则是指:不能强迫用户去依赖那些他们不使用的接口。换句话说,使用多个专门的接口比使用单一的总接口总要好。
这里牵涉到里氏替换原则中的问题,既然里氏替换原则中为允许有这样的变化,那么,这样的变化如何处理?
PHP不同于C++,不允许多继承,即不允许同时继承两个类。但可以继承的同时再实现相关的接口,一个类,可以同时实现多个接口。因此,多个接口可以在需要时实现组合,这样用户就不会被强迫依赖那些他们不使用的接口。
DIP: The Dependency Inversion Principle 依赖倒置原则
依赖倒置原则是指:1. 高层模块不应该依赖于低层模块,二者都应该依赖于抽象;2. 抽象不应该依赖于细节,细节应该依赖于抽象
其中的高层模块,实际是指核心代码,低层模块是指具体代码。很显然,这里涉及到的是核心代码调用具体代码的模式。要让高层模块不应该依赖于低层模块,那就是核心代码中不能涉及具体的一切,不能因任一具体在代码中增加任何的if else类似的控制结构。
二者都应该依赖于抽象,则是指,应当将具体的直接纳入到具体的类中,二者之间通过抽象类或接口定义好约定的调用关系。
抽象不应该依赖于细节,细节应该依赖于抽象,实际是指,上面的这些模式,首先要把握,哪些是细节,是具体问题,哪些是核心代码。将变与不变划分清楚。
我们可以看出,如果没有这些原则,很多时间,我们对应用的功能模块划分是很困难的。但如果划分完成,接下来,编写这样的代码也是有难度的。因为,很多时候,我们要面向的是抽象,是一种面向抽象编程。要能够更好的面向抽象,设计模式的运用是必不可少的技能。
(待续)