设计模式(一)——面向对象设计原则

面向对象设计原则是为了支持可维护性复用而诞生。常见的7种面向对象的设计原则:单一职责原则、开闭原则、里氏代换原则、依赖倒转原则、接口隔离原则、合成复用原则、迪米特法则。

单一职责原则(single responsibility principle, SRP)

它用于控制类的粒度大小,一个类只负责一个功能领域的相应职责。实现高内聚、低耦合。

开闭原则(open-close principle, OCP)

一个软件实体应当对扩展开放,对修改关闭。即软件实体应当在不修改原有代码的情况下扩展。为了满足开闭原则,需要对系统进行抽象化设计。

里氏代换原则(Liskov substitution principle, LSP)

所有使用父类(基类)对象的地方一定能用其子类对象进行替换,反之则不成立。
在程序中尽量使用父类进行定义,在运行时再确定其子类类型。减少代码重复,使系统更具有扩展性。
注意:
1)子类的所有方法必须在父类中声明,或子类必须实现父类中声明的所有方法;
2)尽量把父类设计成抽象类或接口,让子类继承父类或实现父接口,并实现在父类中声明的方法,运行时子类替代父类。在扩展系统功能时,无需修改原有子类的代码,增加新功能时,可以通过定义一个新的子类实现。
3) 在Java语言中,在编译阶段,Java编译器会检查一个程序是否符合里氏代换原则。

依赖倒转原则(dependence inversion principle, DIP)

要针对接口编程,而不是针对实现编程。针对抽象层编程,将具体类写在配置文件中,而将具体类的对象通过依赖注入(dependency injection, DI)的方式注入到其它对象。这样一来如果系统对象发生变化,只需要对抽象层进行修改,同时修改配置文件,无需修改系统源代码。
依赖注入是指当一个对象要与其他对象发生依赖关系时,通过抽象来注入所依赖的对象。常用的注入方式有三种:构造注入(通过构造函数来传入具体类的对象)、设值注入(通过setter方法来传入具体类的对象)和接口注入(通过在接口中声明的业务方法来传入具体类的对象)。这些方法在定义时使用的是抽象类型,在运行时再传入具体类型的对象,由子类对象来覆盖父类对象。

接口隔离原则(interface segregation principle, ISP)

使用多个专门的接口,而不使用单一总接口。当一个接口太大时,需要将它分割成一些更小的接口,每个接口承担一种相对独立的角色。这里的接口有两种含义:
1)一个类型所具有的的所有方法特征的集合。这是一种逻辑上的概念,可以把接口理解成角色,一个接口只能代表一个角色,每个角色都有它特定的一个接口,此时这个原则也叫“角色隔离原则”。
2)狭义的特定语言的接口,如Java语言的interface。接口仅为客户端提供需要的行为,客户端不需要的行为则隐藏。在面向对象编程语言中,实现接口就是实现接口中的所有方法啊,因此大的接口使用起来很不方便,灵活性较差。接口应当尽可能小,每个接口只包含一个客户端(如子模块或业务逻辑类)所需的方法即可。

合成复用原则(composite reuse principle, CRP)

在面向对象设计中,有两种方法可以在不同环境中复用已有的设计和实现,即通过对象组合(组合/聚合关系)和通过继承。但应该尽量使用对象组合,而不是使用继承达到复用。
1)对象组合可以使系统更加灵活,降低类之间的耦合度,一个类的变化对其它类造成的影响相对较小。如果两个类之间是“B has A”的关系用对象组合。
2)其次才考虑使用继承进行复用,使用继承时需要严格遵循里氏代换原则。继承复用的主要问题是会破坏系统的封装性,因为会将父类的实现细节暴露给子类。如果父类发生改变,则子类的实现也必须改变,但从父类继承的实现是静态的,不可能在运行时发生改变,没有足够的灵活性。如果两个类之间是“B is A”的关系使用继承。

迪米特法则(law of demeter, LoD)

一个软件实体应当尽可能少地与其他实体发生相互作用。迪米特法则可以降低系统耦合度,使类与类之间保持松散的耦合关系。
如果一个对象需要调用另一个对象的某一个方法,引入一个合理的第三者来降低现有对象之间的耦合度。