设计模式
设计原则
单一职责原则
里式替换原则
依赖倒置原则
接口隔离原则
迪米特法则
开闭原则
创建型模式
Abstract Factory (抽象工厂) — 对象创建型模式
Builder (生成器) — 对象创建型模式
Factory Method (工厂方法) — 对象创建型模式
Prototype (原型) — 对象创建型模式
Singleton (单例) — 对象创建型模式
结构型模式
Adapter (适配器) — 类对象结构型模式
Bridge (桥接) — 对象结构型模式
Composite (组合) — 对象结构型模式
Decorator (装饰) — 对象结构型模式
Facade (外观) — 对象结构型模式
Flyweight (享元) — 对象结构型模式
Proxy (代理) — 对象结构型模式
行为模式
Chain of Responsibility (职责链) — 对象行为型模式
Command (命令) — 对象行为型模式
Interpreter (解释器) — 类行为型模式
Iterator (迭代器) — 对象行为型模式
Mediator (中介者) — 对象行为型模式
Memento (备忘录) — 对象行为型模式
Observer (观察者) — 对象行为型模式
State (状态) — 对象行为型模式
Strategy (策略) — 对象行为型模式
Template Method (模板方法) — 类行为型模式
Visitor (访问者) — 对象行为型模式
设计原则
单一职责原则
单一职责原则(Single Responsibility Principle),简称是SRP
定义:There should never be more than one reason for a class to change(应该有且仅有一个原因引起类的变更)
单一职责原则要求一个接口或类只有一个原因引起变化,也就是一个接口或类只有一个职责,它就负责一件事情
单一职责原则有什么好处
类的复杂性降低,实现什么职责都有清晰明确的定义
可读性提高,复杂性降低
可维护性提高,可读性提高
变更引起的风险降低,变更是必不可少的,如果接口的单一职责做得好,一个接口修改只对相应的实现类有影响,对其他的接口无影响,这对系统的扩展性、维护性都有非常大的帮助
注意:单一职责原则提出了一个编写程序的标准,用“职责”或“变化原因”来衡量接口或类设计得是否优良,但是“职责”和“变化原因”都是不可度量的,因项目而异,因环境而异
单一职责适用于接口、类,同时也适用于方法,一个方法尽可能做一件事, 类的单一职责确实受非常多因素的制约
单一职责原则,建议是接口一定要做到单一职责,类的设计尽量做到只有一个原因引起变化。
里氏替换原则
氏替换原则(Liskov Substitution Principle,LSP)
第一种定义,也是最正宗的定义:If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T,the behavior of P is unchanged when o1 is substituted for o2 then S is a subtype of T.(如果对每一个类型为S的对象o1,都有类型为T的对象o2,使得以T定义的所有程序P在所有的对象o1都代换成o2时,程序P的行为没有发生变化,那么类型S是类型T的子类型。)
第二种定义:Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it.(所有引用基类的地方必须能透明地使用其子类的对象。)
第二个定义是最清晰明确的,只要父类能出现的地方子类就可以出现,而且替换为子类也不会产生任何错误或异常,使用者可能根本就不需要知道是父类还是子类。但是,反过来就不行了,有子类出现的地方,父类未必就能适应
里氏替换原则为良好的继承定义了一个规范,一句简单的定义包含了4层含义
子类必须完全实现父类的方法, 注意:如果子类不能完整地实现父类的方法,或者父类的某些方法在子类中已经发生“畸变”,则建议断开父子继承关系,采用依赖、聚集、组合等关系代替继承
子类可以有自己的个性, 里氏替换原则可以正着用,但是不能反过来用。在子类出现的地方,父类未必就可以胜任
覆盖或实现父类的方法时输入参数可以被放大, 父类方法的输入参数是HashMap类型,子类的输入参数是Map类型,也就是说子类的输入参数类型的范围扩大了,子类代替父类传递到调用者中,子类的方法永远都不会被执行。这是正确的,如果你想让子类的方法运行,就必须覆写父类的方法。大家可以这样想,在一个Invoker类中关联了一个父类,调用了一个父类的方法,子类可以覆写这个方法,也可以重载这个方法,前提是要扩大这个前置条件,就是输入参数的类型宽于父类的类型覆盖范围。这样说可能比较难理解,我们再反过来想一下,如果Father类的输入参数类型宽于子类的输入参数类型,会出现什么问题呢?会出现父类存在的地方,子类就未必可以存在,因为一旦把子类作为参数传入,调用者就很可能进入子类的方法范畴
覆写或实现父类的方法时输出结果可以被缩小, 父类的一个方法的返回值是一个类型T,子类的相同方法(重载或覆写)的返回值为S,那么里氏替换原则就要求S必须小于等于T,也就是说,要么S和T是同一个类型,要么S是T的子类
采用里氏替换原则的目的就是增强程序的健壮性,版本升级时也可以保持非常好的兼容性。即使增加子类,原有的子类还可以继续运行。在实际项目中,每个子类对应不同的业务含义,使用父类作为参数,传递不同的子类完成不同的业务逻辑