由 Robert Martin提出的S.O.L.I.D 原则,用来更好编写面向对象程序,更灵活应对变化。
S - Single Responsibility Principle 单一职责,简称SRP
这个我前面几篇文章刚刚写:对象的责任与职责
比如:报表的内容和报表的格式都会变化改变,但是这两种变化的性质不同,一个是实质内在,一个是表面上的,SRP认为这是问题的两个方面,其实代表不同的职责,应该将它们分离放入不同的类或模块中,而不应该放在一起,否则的话,因为不同原因发生变化,导致对方变动,比如报表格式变新的样式,这个变化是不应该涉及到内容的。
这个模式和GoF模式中职责链模式Chain-of-responsibility pattern类似,体现了职责分离,分散关注Separation of concerns等OO思想。当然,也只有认识到事物高凝聚Cohesion本质,才能发现耦合。
O - Open/Closed Principle 开闭原则
我一直使用变化和不变来说明,封装不变部分,开放变化部分,一般使用接口继承实现方式来实现“开放”应对变化,说大白话就是:你不是要变化吗?,那么我就让你继承实现一个对象,用一个接口来抽象你的职责,你变化越多,继承实现的子类就越多。
L - Liskov Substitution Principle 里氏替换原则 简称LSP
这是一个针对行为职责可替代的原则,如果S是T的子类型,那么S对象就应该在不改变任何抽象属性情况下替换所有T对象。这里的抽象属性是指对象的字段属性。
我们使用接口时经常碰到一个问题,需要使用接口子类中的方法,而接口中没有这个方法,那么只能要么修改接口,要么将接口downcast为具体子类。为什么会出现这个尴尬现象?有几种情况导致,其中一种情况是是将当前的类重构到接口时,没有将类中所有方法extract到接口中,可能因为这些被你漏掉的方法不属于当前接口,那么,它又违背了单一职责原理,说明你当前这个类的方法设计得又不合理。
所以,如果单一职责设计的足够好,那么LSP原则则是检验的方法。LSP原则是对对象职责和协作的一种检验约束方法,此外还有DBC(design by contract)原则,为了保证实现接口的子类职责行为的约束,DBC三要素都必须被重视满足:
1.Preconditions前置条件不能在子类中被强化。
2.Postconditions后置条件不能在子类中被弱化。
3.子类自身不变性Invariants必须在子类自己中封装满足。这也是前面“不改变任何抽象属性”的意思。
I - Interface Segregation Principle接口分离 简称ISP原则
这类似General Responsibility Assignment Software Patterns中高凝聚原则 High Cohesion Principle ,这是解决胖接口,接口很大很丰富,就要进行解耦切分,把一个接口切分为多个接口,把一个大的职责切分为小职责以及这些职责之间的协作交互。
切分时必须依据高凝聚原则,单一职责进行切分。
这个原则起源于施乐公司,他们需要建立了一个新的打印机系统,可以执行诸如装订的印刷品一套,传真多种任务。此系统软件创建从底层开始编制,并实现了这些任务功能,但是不断增长的软件功能却使软件本身越来越难适应变化和维护。每一次改变,即使是最小的变化,有人可能需要近一个小时的重新编译和重新部署。这是几乎不可能再继续发展,所以他们聘请罗伯特Robert帮助他们。
他们首先设计了一个主要类Job,几乎能够用于实现所有任务功能. 只要调用Job类的一个方法就可以实现一个功能,Job类就变动非常大,是一个胖模型啊,对于客户端如果只需要一个打印功能,但是其他无关打印的方法功能也和其耦合,ISP原则建议在客户端和Job类之间增加一个接口层,对于不同功能有不同接口,比如打印功能就是Print接口,然后将大的Job类切分为继承不同接口的子类,这样有一个Print Job类,等等。
D - Dependency Inversion Principle 依赖反转原则 DIP
a.高级别模块不应依赖于低层次的模块。双方应依赖于抽象。
b.抽象不应当取决于细节。细节应该依赖抽象。
Dependency Injection 依赖注入模式也是属于这种类型变种,GoF设计模式中适配器模式中,高层次类只依赖于adapter接口. 而被适配者低层次也只依赖adapter接口。
SOLID原则如今在DCI架构中能够得到真正实现和发展