# 设计模式

# 六大原则

  • 单一职责原则:一个类只负责一个功能领域中的相应职责,或者可以定义为:就一个类而言,应该只有一个引起它变化的原因。

    There should never be more than one reason for a class to change.

    不同的类具有不同的职责,各司其职。如果一个类拥有了两种职责,那么就最好将其分为两个类。

  • 里氏替换原则(Liskov Substitution Principle):所有引用基类的地方必须能够透明地使用其子类的对象。

    Functions that use pointers of references to base classes must be able to use objects of derived classes without knowing it.

    它是开闭原则的具体实现手段之一。里氏替换原则的核心是抽象,而抽象又依赖于继承这个特性。继承的优点有:1. 代码重用,减少创建类的成本,每个子类拥有父类的属性和方法;2. 子类和父类基本相似,但是又于父类有区别;3. 提高代码的可拓展性。继承的缺点有:1. 继承是侵入性的,只要继承了就必须拥有父类的所有属性和方法;2. 可能会造成子类代码的冗余,灵活性降低,因为子类必须拥有父类所有的属性和方法。里氏替换原则和开闭原则是相互依赖的,通过里式替换来达到对扩展的开发,对修改的关闭效果。

  • 依赖倒转原则(Dependence Inversion Principle):高层模块不应该依赖底层模块,二者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象。

    High level modules should not depends upon low level modules. Both should depend upon abstractions. Abstractions should not depends upon details. Details should depend upon abstractions.

    在Java中,抽象是指接口或者抽象类(Abstract),两者都是不可以被实例化的(不能被new);而细节指的是具体的实现类,实现接口或者抽象类,是可以被实例化的。高层模块指的就是调用端,低层模块就是具体的实现类。所以依赖倒转原则在Java中的表现就是,模块间的依赖通过抽象产生,实现类之间是不会产生依赖关系的,其依赖关系是通过接口或者抽象类产生的。如果类与类之间直接依赖细节,那么可能会直接耦合。需要修改时,就会同时修改依赖者的代码,限制了可拓展性。

  • 接口隔离原则(Interface Segregation Principle):客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上

    The dependency of one class to another one should depend on the smallest possible interface.

    接口隔离原则的建立最小的接口是指建立单一接口,不要建立庞大臃肿的接口;尽量细化接口,接口中的方法尽量要少。即为各个类建立专用的接口,而不要试图建立一个很庞大的接口供所有的依赖它的类调用。需要注意以下几点:

    • 接口尽量要小,但是也有限度。对接口进行细化可以提高程序设计的灵活性;但是如果太小了,则会造成接口数量过多,设计复杂化;
    • 为依赖接口的类定制服务,只暴露给调用的类需要的方法,它不需要的方法则隐蔽起来。这样才能建立最小的依赖关系;
    • 提高内聚,减少对外交互。接口方法尽量少用public修饰,接口是对外的承诺,承诺越少对系统开发越有利,变更风险也会越少。
  • 迪米特原则(Law of Demeter,最少知道原则):一个对象应该对其它对象保持最少的了解,即一个软件实体应当尽可能少地与其它实体发生相互作用

    Only talk to your immediate friends.

    一个类应该对自己需要耦合或者调用的类了解最少,类的内部如何实现与调用者或者依赖关系越密切,耦合度就越大。当一个类发生变化时,对另外一个类的影响也越大。即低耦合、高内聚

    • 在类的划分上,应当尽量创建松耦合的类。类之间的耦合度越低,当一个类修改时,就不会对关联的类造成太大的波及;
    • 在类的结构设计上,每一个类都应当尽量降低其成员变量和成员函数的访问权限;
    • 在对其它类的引用上,一个类对其它对象的引用应当降到最低;
  • 开放封闭原则(Open Close Principle):对扩展开放,对修改关闭

    Software entities like classes, modules and functions should be open for extension but closed for modifications.

    类、模块、函数等应该是可以拓展的,但是不可修改。开闭原则指导我们,当软件发生变化时,应该尽量通过拓展的方式来实现变化,而不是通过修改已有代码来实现(OCP原则并不是说绝对不可以修改原始的类)。

  • 合成复用原则(六大原则之外):尽量使用对象组合,而不是继承来达到复用的目的。

    合成复用原则就是在一个新的对象里通过关联关系(包括组合关系和聚合关系)来使用一些已有的对象,使之成为新对象的一部分,新对象通过委派调用已有对象的方法达到复用功能的目的。即首先考虑组合/聚合,可以使系统更灵活,降低类与类之间的耦合度,其次才考虑继承。在使用继承时,也要严格遵循里氏替换原则。

# 23种设计模式

23种设计模式分为三大类(分类不同版本有不同的数量):

  • 创建型模式(6种):单例模式、简单工厂模式、工厂方法模式、抽象工厂模式、建造者模式、原型模式

    创建型模式对类的实例化过程进行了抽象,能够将模块中对象的创建和对象的使用分离。为了使结构更加清晰,外界对于这些对象只需要知道它们共同的接口,而不清楚具体的细节,使整个系统的设计更加符合单一职责原则。

  • 结构型模式(6种):外观模式、适配器模式、桥接模式、代理模式、装饰者模式、享元模式

    结构型模式描述如何将类或者对象结合在一起形成更大的结构。

  • 行为型模式(11种):职责链模式、命令模式、解释器模式、迭代器模式、中介者模式、备忘录模式、观察者模式、状态模式、策略模式、模板方法模式、访问者模式

    行为型模式是对在不同的对象之间划分责任和算法的抽象化。行为型模式不仅仅关注类和对象的结构,而且重点关注它们之间的相互作用。通过行为型模式,可以更加清晰地划分类与对象之间的职责,并研究系统在运行时实例对象之间的交互。

具体的23种设计模式详见Java基础部分的设计模式分析