单一职责最难界定的就是关于“职责”的定义,往往需要丰富的经验和对业务的认知程度,这也更加容易引起撕逼大战,似乎任何时候都可以拿它来辩论。如设计 ImageLoader 时,图片缓存功能和图片加载逻辑应该分开写在不同的类中。再比如在 Android 开发的早期 MVC 盛行,什么都往 Activity 里面加(网络请求、Adapter、Bean)导致 Activity 臃肿,这是典型的没有遵守单一职责造成的。
下面是违反单一职责原则的反例:
如果这样设计,随着需要实现的不同功能的类越来越多,这样的实现方式就会变成一种继承灾难。
单一职责原则的关键词:
当软件需要变化时,尽量通过扩展的方式来实现变化,而不是通过修改已有代码。通常情况下继承是最简单的方式,因为这种方式能保证已经正常 work 的代码不动,但扩展的方式不仅仅是通过继承,更多的是通过持有抽象来拥抱不同的变化。例如 ImageLoader 可以通过持有一个 ImageCache 的接口类型变量,在使用时需要提供不同缓存策略时,set 方法注入 ImageCache 接口的不同实现,达到不修改已有类的目的。
下面是违反开闭原则的例子:
正确的做法:
开闭原则的关键词:
比如 show(View) 方法传入不同的子类实现 TextView、 ImageView 等,就是使用 View 的不同子类来替换基类 View。再比如 Solider 类持有一把抽象的枪 Gun,使用时再传入具体的实现。因此一般持有抽象的类成员、方法参数都可以认为是里氏替换的例子。里氏替换的核心原理是抽象,且子类必须完全实现父类的方法,否则,应该用组合代替继承。
里氏替换的例子:
代理模式:
里氏代换原则的关键词:
依赖倒置原则一句话就是:面向接口编程,或面向抽象编程。因为类与类之间一旦依赖于细节,就会产生耦合,例如 ImageLoader 中的缓存策略依赖于某一种具体的实现 MemoryCache 而不是 ImageCache 接口,这样需要更换缓存策略时就会不停的修改 ImageLoader 类。
依赖倒置原则的最大好处:降低耦合、隔绝变化
依赖倒置原则的关键词: