面向对象6大设计原则

单一职责原则

  1. 一个类应该只有一个引起变化的原因
    类的每个责任都有改变的潜在区域,超过一个责任,意味着超过一个改变的区域;这个原则告诉我们,尽量让每个类保持单一责任。
  2. 单一职责原则SRP有两个含义:
    避免相同的职责分散到不同的类;避免一个类承担太多的职责。
  3. 为什么要遵守SRP?
    可以减少类之间的耦合;如果一个类承担的职责过多,就等于把这些职责耦合在一起,一个职责的变化可能会削弱或抑制这个类完成其他职责的能力。这种耦合会导致脆弱的设计,当变化发生时,设计会遭受意想不到的破坏;提高类的复用性。
  4. 找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起

开放·封闭原则

类应该对扩展开发,对修改关闭。开放-封闭原则OCP,该原则的基本思想如下:
1. 模块的行为必须是开放的、支持扩展的,而不是僵化的;
2. 对模块功能扩展时,不应该过多影响已有的程序模块;

如何遵守OCP?
1. OCP核心思想就是对抽象编程,而不对具体实现编程,因为抽象相对稳定。
2. 在设计方面充分应用“抽象”和“封装”的思想;
3. 在系统功能编程实现方面应用面向接口的编程。

依赖倒置原则

依赖倒置原则,简单讲就是依赖接口,具体概念如下:上层模块不应该依赖于下层模块,他们共同依赖于一个抽象(父类不能依赖子类,他们都要依赖抽象类);抽象不能依赖具体,具体应该依赖抽象。

要依赖抽象,不要依赖具体类,简称依赖倒置原则
1. 变量不可持有具体类的引用(如果使用new,就会持有具体类的引用。你可以改用工厂方法来避开这样的做法)
2. 不要让类派生自具体类(如果派生自具体类,你就会依赖具体类,请派生自一个抽象类或接口)
3. 不要覆盖基类中已实现的方法(如果覆盖基类已实现的方法,那么你的基类就不是一个真正适合被继承的抽象。基类已有实现的方法,应该由所有的子类共享)

别调用我,我会调用你。说明:我们允许低层组件将自己挂钩到系统上,但是高层组件会决定什么时候和怎么使用这些低层组件。换句话说,高层组件对待低层组件的方式是“别调用我,我会调用你”

迪米特法则LoD

  1. 最少知识原则,如果两个类不必彼此直接通信,那么这两个类就不应当直接的相互作用。如果其中一个类需要调用另一个类的某一个方法,可以通过第三方转发这个调用。
  2. LoD首先强调的前提是在类的结构设计上,每一个类都应当尽量降低成员的访问权限,就是说每个类包装好自己的private状态,不需要让别的类知道的字段或行为就不用公开。
  3. 根本思想是强调类之间的松耦合。类之间耦合度越低,越有利于复用,一个弱耦合的类被修改,不会对有关系的类有坏影响。

接口隔离原则ISP

指客户端不应该被强迫实现一些他们不会使用的接口,应该把胖接口中的方法分组,然后用多个接口代替他,每个接口服务于一个子模块。简单来说,就是使用多个专门的接口比使用单个接口更好。

ISP主要观点如下:
1. 一个类对另外一个类的依赖性应当建立在最小的接口上。
2. ISP可以达到不强迫客户(接口使用方)依赖于他们不用的方法,接口的实现类应该只呈现为单一职责的角色;
3. ISP还可以降低客户之间的相互影响,某一个客户接口需求的变换尽可能少影响其他客户;
4. 客户端程序不应该以来它不需要的接口方法。

替换原则LSP

替换原则LSP,由于在面向对象编程过程中,由于继承实现过于简单,导致很多继承设计不合理。LSP指出:子类型必须能够替换掉他们的父类型、并出现在父类能够出现的任何地方。

如何遵守LSP?
1. 父类的方法都要在子类中实现或者重写,并且派生类只实现其抽象类中声明的方法,而不应当给出多余的方法定义或者实现;
2. 在客户端程序中只应该使用父类对象而不应当直接使用子类对象,这样可以实现运行期绑定(动态多态)。


面向对象与面向过程一个核心区别是如何分配职责。过程式编码表现为一系列命令和方法的连续调用,控制代码根据不同的条件执行不同的职责,这种自上而下的控制方式导致了重复和相互依赖的代码遍布与整个项目。面向对象编程则将职责从客户端代码中移到专门的对象中,尽量减少相互依赖。

David Chelimsky指出:在过程式编程中,过程完全写在一处,即按顺序编写一系列指令。而在面向对象中,过程表示为对象之间的一系列消息。一个对象想另一个对象发送消息,就会完成过程的一部分,以此类推。要修改一个过程,只需要重新组织消息序列,而不是改变一个过程。

面向对象系统中的数据处理,Allen Holub《Holub on Patterns》建议:不要直接请求完成一个工作所需的信息/数据,而应当请求拥有这个信息的对象为你完成工作。

内聚:是一个模块内部各成分之间相关联程度的度量,用来度量一个类或模块紧密地达到单一目的或责任。
当一个模块或一个类被设计成只支持一组相关的功能时,我们就说它具有高内聚;反之,当被设计成支持一组不相关的功能时,我们就说它具有低内聚。
理想情况下,应该使各组件职责清晰、分工明确,如果代码间的关联范围太广,维护就会很困难,因为需要在修改某部分代码的同时修改相关的代码。

耦合:是指不同模块间的依赖程度,当系统各部分代码紧密绑在一起时,就会产生紧密耦合,这时一个组件的变化会迫使其他部件随之改变。
1. 低耦合指模块间尽可能独立存在,接口尽量少而简单;
2. 解耦就是解除模块间的依赖;
3. 为了交互对象之间的松耦合设计而努力。

正交:指将职责相关的组件紧紧组合在一起,而与外部系统环境隔开,保持独立。
正交主张重用组件,期望不需要任何特殊配置就能把一个组件插入到新系统中。这样的组件有明确的与环境无关的输入和输出,即每个组件发生的改变都只影响到其自身。

封装:就是对客户端代码隐藏数据和功能,也是面向对象的重要概念之一;
要实现封装,最简单的办法就是将属性定义为private或protected。多态是另外一种形式的封装。

多态:指一个公用接口后面维护多个实现,是面向对象系统的基本特性之一;

继承:是一种“是、像”的关系;
组合:是一种“需要”的关系;
多用组合,少用继承,组合优于继承,可提高代码的可重用性;
运行时组合对象所达到的灵活性非常高,但是代码可读性会下降,而继承是应对变化的环境及上下文设计的有效方式,然而它会限制灵活性,尤其当类承担多重责任的时候。

重载:PHP的重载与JAVA不同,JAVA的重载是指一个类可以定义参数列表不同但方法名相同的多个方法;PHP的重载指动态创建类属性和方法;

我们应该如何定义类呢?

最好的办法是让一个类只有一个主要的职责,并且任务要尽可能独立。
我们可以把类的职责用多个词来形容,最好不要超过25个词,不要用到词“且”或者“或”。
如果描述类的句子太长或者太复杂,则应该考虑拆分类。

4个方面来衡量一个类设计的好坏:

  1. 代码重复
  2. 类知道的太多了,比如全局变量的使用
  3. 万能的类,职责太多了
  4. 条件语句,出现条件语句的时候,考虑是否用多态解决,如果不行则要拆分