面向对象设计的原则
一共有七个,这个比较常见,然后书中有关于使用频率的介绍,这个我觉得也很实用。
设计原则名称 | 定义 | 频率 |
---|---|---|
单一指责原则 | 一个类只负责一个功能领域中的相应职责 | ⭐⭐⭐⭐ |
开闭原则 | 软件实体应对扩展开放,而对修改关闭 | ⭐⭐⭐⭐⭐ |
里氏代换原则 | 所有引用基类的地方都能够透明的使用其子类的对象 | ⭐⭐⭐⭐⭐ |
依赖倒转原则 | 抽象不应该依赖于细节,细节应该依赖于抽象 | ⭐⭐⭐⭐⭐ |
接口隔离原则 | 使用多个专门的接口,而不使用单一的总接口 | ⭐⭐ |
合成复用原则 | 尽量使用对象组合,而不是继承来达到复用的目的 | ⭐⭐⭐⭐ |
迪米特法则 | 一个软件实体应当尽可能少的与其他实体发生相互作用 | ⭐⭐⭐ |
单一指责
一个类只负责一个功能领域中的相应职责。也可以定义:就一个类而言,应该只有一个因其他变化的原因。
软件系统中,一个类承担的指责越多,包括他的模块,成员变量,方法等等,那他被复用的可能性就越小,耦合性也越高。这种情况下,就需要把职责分离,分成不同的类或者接口去实现。如果多个职责总是同时改变的,那可以把他封装成一个类。
开闭原则
一个软件实体应当对扩展开放,对修改关闭。即一个软件实体应尽量不修改原有代码的条件上进行扩展。
这个原则的目的是为了在增加需求的时候,尽可能保证设计框架的稳定,减少重构,只需要扩展。这个对大型的软件来说非常重要。为了满足这个原则,需要对系统进行抽象化设计,抽象化是开闭原则的关键。通过面向对象语言的接口和抽象类机制,定义系统的抽象层,再通过具体类进行扩展。如果需要修改系统的行为,可无需对抽象类进行任何改动,只需要增加新的具体类来实现新的业务功能即可,实现在不修改已有代码的基础上扩展系统的功能,达到开闭原则的要求。(大部分设计模式都符合这个原则)
里氏代换原则
这个我个人认为是相对最难理解的,顾名思义,就是代换,代换什么呢,就是引用父类对象的地方都可以使用子类的对象
当一个子类的实例应该能够替换任何其超类的实例时,它们之间才具有is-A关系
好比我喜欢动物,那我一定喜欢狗,但是反过来不一定,我喜欢狗,不一定喜欢所有动物。
依赖倒转原则(这个和Spring紧密相关)
抽象不应该依赖于细节,细节应当依赖于抽象。换言之,要针对接口编程,而不是针对实现编程。
这个原则要求在程序源代码中传递参数是或在关联关系中,尽量引用层次搞的抽象层,即使用接口和抽象类进行变量类型声明/参数类型声明/方法返回类型声明,以及数据类型的转换等,而不要用具体的类来做这些事情。(多态????)
在引入抽象层之后,系统的灵活性会更高。在程序中尽量使用抽象层进行编程,具体类写在配置文件中,当系统行为发生变化时,只需要扩展抽象层,而不许修改原有系统的代码(满足开闭原则)。
另外,实现依赖倒转时,需要将实现类的具体对象通过依赖注入(Dependency Injection DI)的方式注入到其他对象中。
接口隔离原则
使用多个专门的接口,而不是使用单一的总接口,即客户端不应该依赖那些它不需要的接口。
就是说接口应该细化(但也不能太小,这个好像在clean code一书中有提到),这样实现起来方便,如果接口过大,这样很容易造成重构。- - 同时如果要在大接口上添加功能,这样会看到其他很多功能,破坏了程序的封装性,最主要的就是大大降低了灵活性。
合成复用原则
应尽量使用对象组合,而不是继承来达到复用的目的。
组合和聚合可以减少类与类之间的耦合度,一个类的变化对其他类造成的影响相对较少;其次才考虑继承,在使用继承时,需要严格遵循历史代换原则,有效使用继承会有助于对问题的理解,降低复杂度,随意使用的话,就会提升系统复杂度和维护难度。
不建议用继承原因就是继承会破坏系统的封装性,因为继承会把基类的实现细节全部暴露给子类,子类则可见,如果基类发生改变,那么子类的实现也不得不改变;另外基类继承而来的实现是静态的,程序在运行时无法进行改变。
同时组合或聚合关系可以使一个对象变成新对象的一部分,而这个新对象只能调用这个对象,不知道这个对象的具体实现的,也就是所谓的黑箱调用。另外,合成复用可以在程序运行时动态进行。
迪米特法则
一个软件实体应当尽可能少的与其他实体发生相互作用。
(就是减少对象之间的交互)目的时为了降低系统的耦合度,保持类与类之间松散的耦合关系。