在对程序进行更新迭代的过程中,应当合理的避免修改类或方法的内部代码,而是优先选择通过继承、扩展等方式来实现。简而言之,就是:对扩展开放,对修改关闭。
在实现子类的定义时,应该让它完全拥有替代父类进行工作的能力。简而言之,就是:子类对外要具与父类一致的方法或接口。
在对象或类的依赖关系定义上,父类或者其他上层实现不应该依赖于子类或者其他下层实现,通过这样,来避免依赖关系的耦合。
在程序结构和依赖关系的定义上,要将类的功能职责充分理清,尽力减少类之间的耦合。避免对某个类进行修改时,牵一发动全身的连锁反应。
在对外接口的定义上,要避免庞大而臃肿的接口,而是进行责任细化的区分,避免冗余的代码实现。这对于提高内聚,提升系统灵活度是非常有效果的。
在分配类的职责和建立依赖关系时,应该只关注于自身的功能实现和周围与之接触类的交互方式。避免类去考虑整个系统结构和处理流程,让类的职责清晰化,让系统的耦合度降低。
在扩展功能的时候,要优先考虑水平形式的新增类或方法,而不是通过继承去实现。也就是通过功能的组合实现类,而不是通过基础去实现新的功能。这样可以提高类的可扩展性,减少系统的层次。
- 对接口编程,不要对实现编程
- 使用对象之间的组合,减少对继承的使用
- 抽象用于不同的事物,而接口用于事物的行为
- 开闭原则:对扩展开放,对修改封闭
- mean: 实例的内部不可修改,但可以增加新功能
- 依赖倒转:对接口编程,依赖于抽象而不依赖于具体
- mean: 就是把公共的拿出来,定义成抽象类、接口、抽象方法,然后大家再去实现这个抽 象,实现的方法各有不同,各个实体相互独立没有依赖,各个实体离开谁都能活
- 接口隔离:使用多个接口,而不是对一个接口编程,去依赖降低耦合
- mean: 就是抽象再抽象
- 最少知道:减少内部依赖,尽可能的独立
- mean: 实现依赖注入容器,把依赖的实体注入到一个实例(所谓容器)
- 合成复用:多个独立的实体合成聚合,而不是使用继承
- mean:尽可能不用继承,使用以上三种方式构成代码结构
- 里氏代换:超类(父类)出现的地方,派生类(子类)都可以出现
- mean:能用父类实现的子类也能实现
- 通过所有测试:及需求为上
- 尽可能的消除重复:高内聚低耦合
- 尽可能的清晰表达:可读性
- 更少代码元素:常量,变量,函数,类,包 …… 都属于代码元素,降低复杂性
- 以上四个原则的重要程度依次降低
核心:高内聚松耦合(单一职责),外部依赖,实体对抽象编程,抽象就是分层
DIP 是一种软件设计的指导思想。传统软件设计中,上层代码依赖于下层代码,当下层出现变动时, 上层代码也要相应变化,维护成本较高。而 DIP 的核心思想是上层定义接口,下层实现这个接口, 从而使得下层依赖于上层,降低耦合度,提高整个系统的弹性。这是一种经实践证明的有效策略。
IoC 就是 DIP 的一种具体思路,DIP 只是一种理念、思想,而 IoC 是一种实现 DIP 的方法。 IoC 的核心是将类(上层)所依赖的单元(下层)的实例化过程交由第三方来实现。 一个简单的特征,就是类中不对所依赖的单元有诸如
$component = new yii\component\SomeClass()
的实例化语句。
DI 是 IoC 的一种设计模式,是一种套路,按照 DI 的套路,就可以实现 IoC,就能符合 DIP 原则。 DI 的核心是把类所依赖的单元的实例化过程,放到类的外面去实现。
当项目比较大时,依赖关系可能会很复杂。 而 IoC Container 提供了动态地创建、注入依赖单元,映射依赖关系等功能,减少了许多代码量。 Yii 设计了一个 yii\di\Container 来实现了 DI Container。
Service Locator 是 IoC 的另一种实现方式, 其核心是把所有可能用到的依赖单元交由 Service Locator 进行实例化和创建、配置, 把类对依赖单元的依赖,转换成类对 Service Locator 的依赖。 DI 与 Service Locator 并不冲突,两者可以结合使用。 目前,Yii2.0 把这 DI 和 Service Locator 这两个东西结合起来使用,或者说通过 DI 容器,实现了 Service Locator。