五项基本原则
- The Single-Responsibility Principle (SRP):单一职责原则
一个类由一个原因进行改变。
2. The Open/Closed Principle (OCP):开放-封闭原则
Software entities (classes,modules,functions,etc.) should be open for extension but closed for modification.3. The Dependency-Inversion Principle (DIP):依赖倒置原则
A. High-level modules should not depend on low-level modules. Both should depend on abstractions.高层模块不依赖于底层模块。应该依赖于抽象。
B. Abstractions should not depend upon details. Details should depend upon abstractions.
抽象不应该依赖于细节,细节应该依赖于抽象
4. The Liskov Substitution Principle (LSP):Liskov替换原则
Subtypes must be substitutable for their base types。子类型必须能够替换他们的父类型。
5. The Interface Segregation Principle (ISP):接口隔离原则
Clients should not be forced to depend on methods they do not use.不应该强制客户端使用他们不需要使用的方法。
SRP
例1:调制解调器
调制解调器接口实际上包含了两个功能,连接(Dial,Hangup),数据交互(send,Recv),根据SRP原则,应该将其重构为两个接口,DataChanel与Connection,一个用于数据交互的功能,一个用于解调器连接的功能。
例2:领域逻辑与持久化
在这个领域对象当中,即有业务逻辑的方法又有持久化的方法,不符合SRP原则。
基于SRP原则将其分离出两个类,一个用于领域逻辑,一个用于持久化。优点:在开发和维护阶段,使得关注点较少,便于开发以及维护。
OCP
例1:绘图程序
譬如一个绘图程序,有一个RenderView主要去绘制各种图形。
switch(ShapeType){ case Circle: DrawCircle(); break; case Rectangle: DrawRectangle(); break; }
当我再添加一种图形三角形时,必须修改源代码,在switch语句中添加对三角形的代码处理。(重构中的一种坏味道:Switch)此时已经不符合OCP原则,对扩展是不开放的,需要修改源代码。
通过引入接口Shape,在渲染RenderView类中绘制ShapeList中的Shape,当我们再添加一种新图形的时候,简单继承Shape接口,实现Draw方法即可。对于现有的类,RenderView、Shape、ShapeList根本不必做修改。此时,完美的符合了OCP原则,对扩展新图像开放,对于修改现有的代码进行关闭。
DIP
例1:层依赖
在例子中UI层依赖于Logic层,Logic层依赖于persistence层,实际上违反了DIP原则,因为依赖于细节。在每层中定义所需要的接口,然后每层的细节都依赖于接口,然后下层根据接口进行实现,实际上每层依赖于抽象,而不依赖于细节。
对于每层的接口放置问题,可以根据情况而定。
ISP
例1:取款机UI分离
在一个取款机的例子中,Deposit为存款,Withdrawal为取款,Transfer为转账。在UI接口中包含存款,取款,转账的接口。当在DepositTransaction中使RequestDepositAmt接口,但是对于其他的接口也具有可见性。在此处因为较为明显,但是在实际当中并不像例子中那么明显时候,就会造成选择疑惑,以及开发错误,此外在开发UI的实现时,实际上也具有了多种功能的实现,也不符合SRP原则。
通过对接口的分离,首先接口特别清晰,不会造成接口的误用,使得程序代码更清晰易懂。
LSP
What is wanted here is something like the following substitution property: If for each object o1 of type Sthere is an object o2 of type T such that for all programs P defined in terms of T,the behavior of P isunchanged when o1 is substituted for o2 then S is a subtype of T.
对于每一个类型为S的对象o1,都有类型为T的o2,使得以T定义的所有程序P在所有对象o2都代换成o1时,程序p的行为没有变化,那么S是T的子类型。
例1:
struct Point {double x,y;} public enum ShapeType {square,circle}; public class Shape { private ShapeType type; public Shape(ShapeType t){type = t;} public static void DrawShape(Shape s)//此处为static方法,不能使用多态 { if(s.type == ShapeType.square) (s as Square).Draw(); else if(s.type == ShapeType.circle) (s as Circle).Draw(); } } public class Circle : Shape { private Point center; private double radius; public Circle() : base(ShapeType.circle) {} public void Draw() {/* draws the circle */} } public class Square : Shape { private Point topLeft; private double side; public Square() : base(ShapeType.square) {} public void Draw() {/* draws the square */}
在Shape的DrawShape中首先已经破坏了OCP原则。当新添加一种图形时,必须修改DrawShape方法。其次在Circle和Square并不能覆盖Shape中的方法,此时Circle和Square并不能替换Shape。从这个例子可看出只要破坏了LSP原则,也就破坏了OCP原则。
例2:Square与Rectangle
void g(Rectangle r) { r.Width = 5; r.Height = 4; if(r.Area() != 20) throw new Exception("Bad area!"); }当g方法中传入Square的时候,其中的代码就会出错,也就是说Square不能替换成Rectangle。所以也就破坏了LSP原则。对于破坏LSP原则的典型代码,就是传入的父类型强转为特定的子类型,然后进行特殊处理。