【参阅】http://www.cnblogs.com/jiangzhengjun/p/4260969.html
单一职责原则( SRP )——内聚性
定义
单一职责原则的定义: 就一个类页言,应该仅有一个引起它变化的原因 ( There should never be more than one reason for a class to change )。
单一职责原则要求一个接口或类只有一个原因引起变化,也就是 一个接口或类只有一个职责,它就是负责一件事情 。
内聚性:一个模块的组成元素之间的功能相关性。
类的单一职责:将类的属性和行为分离。这里说的属性是指和属性相关的set/get.这样就将其分解为业务对象(BO)和业务逻辑(BL)。
方法的单一职责:一个类可能具有好几个功能,但是不一定就必须将所有的功能放在同一个类中,可以放在不同的接口中,实现接口分离,需要时再次将其组合。【参转】。所以在使用的时候,将所有的功能分类,看应用程序的变化对那些功能会有影响,那些没有。
开 - 闭”原则( OCP )—— 抽象应对一切的变化
Open-Closed Principle : Software entities should be open for extension, but closed for modification. (一个软件实体应当 对扩展开放,对修改关闭 。)
遵循开闭原则设计出的模块具有两个主要的特征。它们是:
1 、“对扩展是开放的”。这意味着模块的行为是可以扩展的,当应用的需求改变时,我们可以对模块进行扩展,使其具有满足那些改变的新行为。换句话说,我们可以改变模块的能。
2 、“对于更改是封闭的”。对模块行为进行扩展时,不必改动模式的源代码或者二进制代码。模块的二进制可执行版本,无论是可链接的库、 DLL 或者 Java 的 ..jar 文件,都无需改动。
开闭原则提高了系统的可维护性(可扩展性 - 增、灵活性 / 可修改性 - 修、可插入性 - 替、可测试性)、可复用性。
里氏代换原则( LSP )
从“开 - 闭”原则中可以看出面向对象设计的关键是抽象,从抽象化到具体化需要使用继承关系,而是否满足继承关系则需要使用里氏代换原则( Liskov Substation principle )来验证。
定义
所有基类(泛指接口与抽象类、还有具体类也可)出现的地方,子类都可以出现。
严格表达是:如果对每一个类型的 T1 的对象 o1 ,都有类型为 T2 的对象 o2 ,使得以 T1 定义的所有程序 P 在所有的对象 o1 都代换成 02 时,程序 P 的行为没有变化,那么类型 T2 是类型 T1 的子类型。换言这,一个软件实体如果使用的是一个基类的话,那么一定适用于子类,而且它根本不能察觉出基类对象和子类对象的区别。
就像长方形不能作为正方形的父类:
否则
public class SmartTest {// 测试
public void resize (Rectangle r ) {
while ( r .getHeight() <= r .getWidth()) {// 正方形会出问题
r .setWidth( r .getWidth() + 1);
}
}
会出错的。
依赖倒转原则( DIP )
定义
Dependency inversion principle :
1 、 高层模块不应该依赖底层模块,两者都应该依赖于抽象层 。
2 、抽象(接口或抽象类)不应该依赖于细节(实现)。
3 、(实现)细节应该依赖于抽象(接口或抽象类)。
【最精简的定义就是: 针对接口编程,不要针对实现编程 。】
使用接口和抽象类进行变量的类型声明、参数的类型声明、方法的返回类型声明,以及数据类型转换等;
不要针对实现编程的意思是说,不应当使用具体类进行变量 的类型声明、参数的类型声明、方法的返回类型声明,以及数据类型转换等。
要保证这一点,一个具体类应当只实现接口和抽象类中声明过的方法,而不应当给出多 余的方法。
什么是“倒转”
什么是“倒转”,要想理解它,我们看看正置吧。依赖正置就是类间的依赖是实实在在的实现类间的依赖,也就是 面向实现编程,这也是正常人的思维方式 ,我要开奔驰就依赖奔驰,要使用苹果笔记本就使用苹果笔记本。而编写程序需要的是对现实世界的事物进行抽象,抽象的结果就是有了抽象类和接口,比如宝马与奔驰抽象成小汽车,苹果笔记本与 IBM 笔记本抽象成笔记本,然后我们的程序就依赖于这些抽象,这代替了人们传统思维中的具体事物间的依赖,“倒转”就是从这里产生的。
也就是说无论是高层模块还是底层模块都没有直接的依赖关系,而是二者同依赖于接口或抽象类。
比如说,某个司机开车不是依赖于某一牌子具体的汽车,而是依赖汽车这一类(抽象出来的)
组合 / 聚合复用原则( CARP )
组合 / 聚合复用原则( Composition/Aggregation Reuse Principle )经常又叫合成复用原则( Composition Reuse Principle 或 CRP )。综是在一个新的对象里使用已有的对象,使之成为新对象的一部分,新的对象通过向这些对象的委派达到复用已有功能的目的。
该原则另一个简短的表述:尽量使用组合 / 聚合,不要使用继承。
组合 / 聚合区别
聚合表示一种弱的“拥有”关系,体现的是 A 对象可以包含 B 对象,但 B 对象不是 A 对象的一部分;
合成则是一种强的“拥有”关系,体现了严格的部分和整体的关系,部分和整体的生命周期一样,一般一个合成的多重性不能超过 1 ,换言这,一个合成关系中的成分对象是不能与另一个合成关系共享的。一个成分对象在同一个时间内只能属于一个合成关系。
组合 / 聚合复用与继承复用的区别
有两种复用的方式:组合 / 聚合复用或继承复用。
组合 / 聚合复用方式可以在运行期内动态的改变,具有很好的灵活性。
继承是在编译时就发生了的,所以无法在运行时改变,没有足够的灵活性。
由于子类会继承父类所有非私有的东西(好比爱她就接受她的一切),如果继承下来的实现不适合解决新的问题,则子类必须重写,这样最终还是限制了复用。
继承会破坏封装特性,因为继承将超类的实现细节暴露给了子类。
如果超类的实现发生改变时,那么子类的实现也会跟着发生改变。
与里氏代换原则区别
“ Is-A ”是严格的分类学意义上的定义,意思是一个类是另一个类的“一种”。而“ Has-A ”则不同,它表示某个角色拥有某一项责任。
里氏代换原则表述的是“ Is-A ”关系,而组合 / 聚合复用表述的是“ Has-A ”关系。
迪米特法则( LOD )
迪米特法则( Law of Demeter )也称最少知识原则( Least Knowledge Principle,LKP ),就是说,一个对象应该对象其他对象有最少的了解。
各种不同的表述:
1、 只与直接的朋友通信。
2、 不要跟“陌生人”说话。
3、 每个软件单位对其他的单位都只有最少的知识,而且局限于那些与本单位密切相关的软件单位。
4、 我的知识(实现细节)你知道得越少越好。
狭义的迪米特法则
狭义的迪米特则要求一个对象仅仅与其朋友发生相互作用。
如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用。如果其中的一个类需要调用另一个类的某个方法的话,可以通过第三者转换。
朋友类的定义
1、 当前对象本身( this )
2、 方法的输入输出参数中的类
3、 成员变量的直接引用对象
4、 集合成员中的元素类
5、 当前对象所创建的对象
接口隔离原则( ISP )
接口隔离原则讲的是:使用多个专门的接口比使用一个总的接口要好。换言这,从一个客户类角度来看,一个类对另外一个类的依赖性应当是建立的在最小的接口之上。
定义
Interface Segregation principle :应当为客户端提供尽可能小的单独的接口,而不要提供大的总接口,即提供职责单一的接口。
1、 客户端不应该被强迫去依赖于它们不需要的接口 。
2、 类间的 依赖关系应该建立在最小的接口上 。
第一点讲的是,客户端需要什么接口我们就提供什么接口,把不需要的接口剔除掉,那就需要对接口进行细化,保证其纯洁性。第二点其实与第一点表达的主旨是一样的,只是一个事物的两种不同描述。
与单一职责原则的不同
上面两点可以概括为一句话:建立专门的接口,不要建立臃肿庞大的接口,或更进一步讲: 接口尽量细化,同时接口中的方法尽量少 。这与单一职责原则不是相同吗?错,接口隔离原则与单一职责原则的审视角度是不相同的, 单一职责要求的是类和接口的职责单一,即功能单一,注重的是“功能”,它是从功能上的划分;而接口隔离原则是从“服务”的角度来看的,它要求的是“服务”专一,是从服务的角度来划分的 。
接口分离指的是将臃肿的接口细化,而不是建立一些无用的接口或者随意的建立接口。