面向对象设计七大原则

匿名 (未验证) 提交于 2019-12-03 00:22:01

最近团队在学习Agile 和 Clean Code。然后对面向对象设计的一些原则进行了一些学习和整理。包括SOLID、合成复用原则与迪米特法则。

可维护性

Robert C.Martin认为一个可维护性较低
的软件设计,通常由于如下四个原因造成:




可复用性

Peter Coad认为,一个好的系统

设计应该具备如下三个性质:




设计原则

先看下设计原则,这里我列举了常说的 SOLID 和 另外两大原则。

设计原则名称简介出处
单一职责原则
(Single Responsibility Principle, SRP)
就一个类而言,应该仅有一个引起它变化的原因。Robert C. Martin《敏捷软件开发:原则、模式与实践》第八章
开闭原则
(Open-Closed Principle, OCP)
软件实体对扩展是开放的,但对修改是关闭的。即在不修改一个软件实体的基础上去扩展其功能。Bertrand Meyer《面向对象软件构造(Object Oriented Software Construction)》
里氏代换原则
(Liskov Substitution Principle, LSP)
Subtypes must be substitutable for their base types。
子类必须能够替换成它们的基类。
Liskov女士《Data Abstraction and Hierarchy
依赖倒转原则
(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 on details. Details should depend on abstractions.

A.高层次的模块不应该依赖于低层次的模块,他们都应该依赖于抽象。

B.抽象不应该依赖于具体实现,具体实现应该依赖于抽象。

作用:降低了客户与实现模块间的耦合

《敏捷软件开发―原则、模式与实践》第十一章
接口隔离原则
(Interface Segregation Principle, ISP)
使用多个专门的接口来取代一个统一的接口。ISP: The Interface Segregation Principle
合成复用原则
(Composite Reuse Principle, CRP)
就是在一个新的对象里面使用一些已有的对象,使之成为新对象的一部分;新的对象通过向这些对象的委派达到复用已有功能的目的。 简单就是:要尽量使用组合,尽量不要使用继承。
迪米特法则
(Law of Demeter, LoD)
又叫作最少知识原则(Least Knowledge Principle 简写LKP),就是说一个对象应当对其他对象有尽可能少的了解,不和陌生人说话。《The Pragmatic Programmer》即《》

单一职责原则

所谓职责,即“变化的原因”。如果你能够想到多于一个的动机去改变一个类,那么这个类就具有多于一个的职责。


开闭原则

该原则要求,软件实现应该对扩展开放,对修改关闭。意思就是说一个软件实体应该通过扩展来实现变化,而不是通过修改已有的代码来实现变化的。

举个例子:

打字员利用打印机打印文档,即可以打印黑白色的,也需要打印彩色的。


之后如果系统增加一种新的打印机:UniversalPrinter。此时就对系统有不小的修改。

相反,如果采用下面的设计,Typist 依赖抽象类 Printer。这样有需求增加变化时,只需要增加一个子类即可。


可能你也发现了,实现开闭原则的关键就是“抽象”。把可能的行为(这里是print)抽象成一个抽象层。之后的扩展都是对这个抽象的实现。


里氏代换原则

里氏代换原则是对开闭原则的补充,它讲的是基类和子类的关系。

“鸵鸟不是鸟",”正方形是长方形"都是理解里氏代换原则的最经典的例子。小学数学的时候就知道,正方形是长方形,即一个长宽相等的长方形。由此,应该让正方形继承自长方形。


此时代码如下:

public class Rectangle {     private int height;     private int width;      // 省略getter setter }

要保证,正方形长和宽始终一样,要覆写两个setter:

public class Square extends Rectangle {     @Override     public void setWidth(int width) {         super.setWidth(width);         super.setHeight(width);     }      @Override     public void setHeight(int height) {         super.setWidth(height);         super.setHeight(height);     } }

两个类在以下代码中,表现不一样:

public class TestSquare {     public static void main(String[] args) {         TestSquare test = new TestSquare();         Rectangle rectangle = new Rectangle();         rectangle.setHeight(5);         rectangle.setWidth(4);         test.zoom(rectangle, 2, 3);          Square square = new Square();         square.setHeight(5);         square.setWidth(4);         test.zoom(square, 2, 3);     }      public void zoom(Rectangle rectangle, int width, int height) {         rectangle.setWidth(rectangle.getWidth() + width);         rectangle.setHeight(rectangle.getHeight() + height);     } }


依赖倒转原则

所谓依赖倒转(Dependency Inversion Principle)有两条:
A.高层次的模块不应该依赖于低层次的模块,他们都应该依赖于抽象。

B.抽象不应该依赖于具体实现,具体实现应该依赖于抽象。

看下图:


在这个图中,从上到下的依赖是传递的,因此底层的任何修改都会影响到上层

再看下面这个图:


这里,上层和下层都依赖抽象层。抽象层是稳定的,它的存在屏蔽了实现层修改带来的影响。


接口隔离原则

客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上。


利用接口隔离原则,将大的接口进行拆分:


这样对每个客户端就隐藏了它不需要的功能。


合成复用原则

这个原则很简单:多用组合,少用继承

先看下


迪米特法则

即最少知道原则。设计模式中的外观模式(Facade)和中介模式(Mediator),都是迪米特法则应用的例子。

这是外观模式的类图,Facade 提供统一的接口,Client只与 Facade 进行通信,与子系统之间的耦合很低。


1.在类的划分上,应该创建有弱耦合的类
1.在类的结构设计上,每一个类都应当尽量降低成员的访问权限
3.在类的设计上,只要有可能,一个类应当设计成不变类
4.在对其他类的引用上,一个对象对其它对象的引用应当降到最低
5.尽量降低类的访问权限

6.不要暴露类成员,而应该提供相应的访问器

缺点:

迪米特法则有个缺点:系统中会产生大量的小方法。


易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!