SRP | The Single Responsibility Principle | 单一职责原则 |
OCP | The Open Closed Principle | 开放封闭原则 |
LSP | The Liskov Substitution Principle | 里氏替换原则 |
ISP | The Interface Segregation Principle | 接口分离原则 |
DIP | The Dependency Inversion Principle | 依赖倒置原则 |
一、单一职责原则(SRP)
从面向对象角度解释这个原则为:"引起类变化的因素永远不要多于一个。" 或者说 "一个类有且仅有一个职责"。
我们通常都说“低耦合,高内聚”。在我看来,这里的"单一职责"就是我们通常所说的“高内聚”,即一个类只完成它应该完成的职责,不能推诿责任,也不可越殂代疱,不能成为无所不能的上帝类。如果你的团队中实施宽松的“代码集体所有权”,在编码的过程中出现许多人同时修改(维护)同一个类的现象,而且成员之间的沟通不够及时,主动和畅通的话,那么时间一长,就很可能出现“承担过多职责”的上帝类。如果你有一个类承担了多项职责,那么你应该重新考虑下这个类,将它进行拆分。class Student {
public $studentName = '';
public $studentRegNo = '';
// Responsibility 1
function insertStudent($aStudent) {
// Logic for inserting a student to database.
}
function selectStudent($aStudent){
//logic
}
function deleteStudent($aStudent){
//logic
}
// Responsibility 2
function generateMarkSheet($aStudent) {
//logic for generating marks.
}
}
我们将这个类拆分开来,这样每个类就都满足单一职责了。
类1:Student 类2:StudentDB 类3:StudentReport
Student 负责get\set name,
StudentDB 负责:selectStudent、deleteStudent
StudentReport负责:generateMarkSheet
举例说明:
让我们看一张图:
想必大家都写过服务器与客户端。他们之间的联系是很紧密的,若服务器端发生变化,很可能使客户端也要做修改,这就导致了无谓的麻烦。因为这样做违背了”开放封闭原则“的设计,导致了 他们之间的高耦合度。
若是改成下图:
这样子做添加了一个抽象服务类,而在客户端类中包含了一个抽象服务类的引用,具体的服务类实现了抽象服务类。这样,当具体的服务类发生修改时,抽象服务类没有改动,进而包含抽象服务类的引用的客户端类就不用发生改动!
在这里,抽象是关键!我觉得之前梳理的java动态代理就是基于这种接口模式。
接口分离原则的核心思想就是:不应该强迫客户程序依赖它们不需要使用的方法。 也就是说,一个接口或者类应该拥有尽可能少的行为(就是少到恰好能完成它自身的职责),这也是保证“软件系统模块的粒度尽可能少,以达到高度可重用的目的。
接口包含太多的方法会降低其可用性,像这种包含了无用方法的"胖接口"会增加类之间的耦合。如果一个类想实现该接口,那么它需要实现所有的方法,尽管有些对它来说可能完全没用,所以这样做会在系统中引入不必要的复杂度,降低代码的可维护性或鲁棒性。
例子可以参见上面的拆分fly接口。
五、依赖倒置原则(DIP)
这个原则的意思是:高层模块不应该依赖底层模块,两者都应该依赖其抽象。其实又是”面向接口编程,不要面向实现编程“的内在要求。
们考虑一个现实中的例子,来看看依赖倒置原则给我们软件带来的好处。
你的汽车是由很多如引擎,车轮,空调和其它等部件组成,对吗?
注意:这里的 Car 就是高层模块;它依赖于抽象接口IToyotaEngine 和 IEighteenInchWheel.
而具体的引擎FifteenHundredCCEngine 属于底层模块,也依赖于抽象接口IToyotaEngine ;
具体的车轮 EighteenInchWheelWithAlloy同样属于底层模块,也依赖于抽象接口IEighteenInchWheel。
上面Car类有两个属性(引擎和车轮列表),
这里,我会将门禁的连接、断开封装在一个接口中。将发送命令封装在另外一个接口中。
这里是连接接口:
interface IConnect{
Connect();
Disconnect();
}
这里是通讯接口:
interface ISendCommand{
sendcommand();
}
这里是我们的门禁接口:
interface IDoor Extends IConnect,ISendCommand{}
我们可以用的Door来实现Idoor接口。如果这个时候硬件工程师告诉我们。通讯方式变了。这个时候。我们只需要添加一个新的类叫NewDoor用不同实现idoor接口。//旧设备
IDoor dr =
new
Door();
dr.Connect();
dr.SendCommand();
dr.DisConnect();
//新设备
IDoor ndr =
new
NewDoor();
ndr.Connect();
ndr.SendCommand();
ndr.DisConnect();
还有两个常被提到的原则:
六、合成/聚合复用原则(Composite/Aggregate Reuse Principle,CARP)
合成复用原则(CARP),在一个新的对象里面使用一些已有的对象,使之成为新对象的一部分;新的对象通过这些向对象的委派达到复用已有功能的目的.这个设计原则有另一个简短的表述:要尽量使用合成/聚合,尽量不要使用继承.
意思就是:在复用对象的时候,要优先考虑使用组合,而不是继承,这是因为在使用继承时,父类的任何改变都可能影响子类的行为,而在使用组合时,是通过获得对其他对象的引用而在运行时刻动态定义的,有助于保持每个类的单一职责原则。
朋友类就是出现在成员变量和方法的输入输出参数中的类,而出现在方法体内部的类则不是朋友类。
该Teacher类中,GroupLeader类是朋友类,而List<Girl>类和Gril类不是朋友类。这就违反了"迪米特法则"
拆分如下:
这样做就把他们拆分开来了,如果需要修改其中任何一个类,都不用对其他的类做太大改动。
http://blog.csdn.net/e5max/article/details/8872182
http://blog.csdn.net/anders_zhuo/article/details/8949566