重温设计模式系列(三)面向对象设计原则

背景数据库

面向对象基础知识,只是给了咱们一个概念,如何更好的设计出良好的面向对象代码,须要有设计原则做为支持。设计原则是核心指导思想,在这些原则的基础上,通过不断的实践,抽象,提炼逐步产生了针对特定问题的设计模式。所以,学好设计模式的基础是掌握基本的设计原则。本文将介绍面向对象经常使用的设计原则。(某些原则,也能够用在系统级,模块级等类型的设计中应用)编程

 

一、代码抽象三原则设计模式

1.1 DRY原则(Don't repeat yourself)微信

意思是:不要重复本身。它的涵义是,系统的每个功能都应该有惟一的实现。也就是说,若是屡次遇到相同的问题,就须要抽象出一个通用的解决方案,不要重复开发相同的功能。架构

用代码举例:若是两个地方须要发送短信的功能,第一个功能是发送提醒短信,第二个是发送验证码短信。则须要把发送短信的公用代码进行提炼。框架

1.2 YAGNI原则( You aren't gonna need it)分布式

意思是:你不会须要它。出自极限编程的原则,指除了核心功能外,其它功能一律不要部署。背后的指导思想是尽快的让代码运行起来。简单理解是尽可能避免没必要要的代码,少就是多。好比:过多的日志打印,过多逻辑检查,过多的异常处理等,若是能简化则简化。ide

1.3Rule Of Three原则模块化

Rule of three 称为"三次原则",指的是当某个功能第三次出现时,才进行"抽象化"。它的含义是:当第一次用到某个功能时,写一个特定的解决方法;第二次又用到的时候,拷贝上一次的代码;第三次出现的时候,才着手"抽象化",写出通用的解决方法。函数

1.4 三原则之间的关系

DRY强调对通用问题的抽象,YAGNI强调快速和简单。Rule Of Three至关于对前两个原则作了一个折衷,提出了应用原则的度量。三原则的折中,有如下几个好处。

(1)省事,避免过分设计:若是只有一个地方用,就不必过分抽象,避免过分设计。

(2)容易发现模式:问题出现的场景多,容易找到通用的部分,方便进行抽象,进而找到模式。

 

二、GRASP原则

GRASP(General Responsibility Assignment Software Patterns),中文名称:“通用职责分配软件模式”,核心是本身干本身能干的事,本身只干本身的 事,也就是职责的分配和实现高内聚。用来解决面向对象设计的一些问题。GRASP一共包括9种模式,给出了最基本的面向对象指导原则,好比:如何决定一个系统有多少对象,每一个对象都包括什么职责。

2.1 Infomation Expert(信息专家)

设计类时,若是一个类有完成某个职责的全部信息,则应该把该职责分配给该类。此时,该类至关于该职责的信息专家。

例如: 常见的网上商店的购物车(ShopCar),须要让每种商品(SKU)只在购物车内出现一次,购买相同商品,只须要更新商品的数量便可。以下图:

针对这个问题须要权衡的是,比较商品是否相同的方法放到哪一个类里来实现呢?分析业务得知须要根据商品的编号(SKUID)来惟一区分商品,而商品编号是惟一存在于商品类的,因此根据信息专家模式,应该把比较商品是否相同的方法放在商品类里。

2.2 Creator(创造者)

用于判断对象的初始化由哪一个类发起,用于肯定正确的依赖关系。实际应用中,符合下列任一条件的时候,都应该由类 A 来建立类 B,这时 A 是 B 的建立者:

a、A 是 B 的聚合

b、A 是 B 的容器

c、A 持有初始化 B 的信息(数据)

d、A 记录 B 的实例

e、A 频繁使用 B

例如:由于订单(Order)是商品(SKU)的容器,因此应该由订单来建立商品。以下图:

这里由于订单是商品的容器,也只有订单持有初始化商品的信息,因此这个耦合关系是正确的且没有办法避免的,因此由订单来建立商品。

2.3 Low coupling(低耦合)

耦合是指两个类之间的依赖程度。低耦合说明两个类之间的依赖程度低。好的耦合是低耦合,有如下好处:

(1)低耦合下降了由于一个类的变化,影响其余类的范围。

(2)使类之间的关系简单,更容易理解。

耦合的场景

a、A 是 B 的属性

b、A 调用 B 的实例的方法

c、A 的方法中引用的 B,例如 B 是 A 方法的返回值或参数。

d、A 是 B 的子类,或者 A 实现 B

例如:Creator 模式的例子里,实际业务中须要另外一个出货人来清点订单(Order)上的商品(SKU),并计算出商品的总价,可是因为订单和商品之间的耦合已经存在了,那么把这个职责分配给订单更合适,这样能够下降耦合,以便下降系统的复杂性。以下图:

这里咱们在订单类里增长了一个 TotalPrice() 方法来执行计算总价的职责,没有增长没必要要的耦合。

2.4 High cohesion(高内聚)

内聚是指类内部职责的紧密程度,高内聚的类是设计良好的类,具有良好的隔离性,当内部变化了,只要接口不改变,不影响其余部分。

例如:一个订单数据存取类(OrderDAO),订单便可以保存为 Excel 模式,也能够保存到数据库中;那么,不一样的职责最好由不一样的类来实现,这样才是高内聚的设计,以下图:

这里咱们把两种不一样的数据存储功能分别放在了两个类里来实现,这样若是将来保存到 Excel 的功能发生错误,那么就去检查 OrderDAOExcel 类就能够了,这样也使系统更模块化,方便划分任务,好比这两个类就能够分配到不一样的人同时进行开发,这样也提升了团队协做和开发进度。

2.5 Controller(控制器)

用于接收和处理系统事件的职责,通常配置给能够表明整个系统的类,通常成功XXController。

有以下原则:

a、系统事件的接收与处理一般由一个高级类来代替。

b、一个子系统会有不少控制类,分别处理不一样的事务。

在MVC架构中,对应的是C。

2.6 Polymorphism(多态)

面向对象的三大特征一下,指一个接口能够有不一样的实现,用于提升系统的灵活性和扩展性,写出高内聚,低耦合的代码。

例如:咱们想设计一个绘画程序,要支持能够画不一样类型的图形,咱们定义一个抽象类 Shape,矩形(Rectangle)、圆形(Round)分别继承这个抽象类,并重写(override)Shape 类里的Draw() 方法,这样咱们就可使用一样的接口(Shape抽象类)绘制出不一样的图形,以下图:

这样的设计更符合高内聚和低耦合原则,虽而后来咱们又增长了一个菱形(Diamond)类,对整个系统结构也没有任何影响,只要增长一个继承 Shape 类就好了。

2.7 Pure Fabrication(纯虚构)

这里的纯虚构跟咱们常说说的纯虚构函数意思相近。高内聚低耦合,是系统设计的终极目标,可是内聚和耦合永远都是矛盾对立的。高内聚觉得这拆分出更多数量的类,可是对象之间须要协做来完成任务,这又形成了高耦合,反过来依然。该如何解决这个矛盾呢?这个时候就须要纯虚构模式,由一个纯虚构的类来协调内聚和耦合,能够在必定程度上解决上述问题。

例如:上面多态模式的例子,若是咱们的绘图程序须要支持不一样的系统,那么由于不一样系统的API结构不一样,绘图功能也须要不一样的实现方式,那么该如何设计更合适呢?以下图:

这里咱们能够看到,由于增长了纯虚构类AbstractShape,不管是哪一个系统均可以经过AbstractShape 类来绘制图形,咱们即没有下降原来的内聚性,也没有增长过多的耦合,可谓鱼肉和熊掌兼得。

2.8 Indirection(间接)

用于隔离或组合两个类之间的交互,避免两个或多个事物之间直接耦合,好比用户类和商品类,二者的职责不一样,若是之间进行直接调用,会造成强耦合(多对多关系),则能够增长一个中间者,实现对调用用户类和商品类的聚合处理。

2.9 Protected Variations(防止变异)

如何设计对象、系统和子系统,使其内部的变化或者不稳定因素不会对其余元素产生不良影响?

预先识别不稳定的因素,抽象为接口,针对接口编程,隔离变化,若是将来发生变化,能够经过接口扩展新功能。例如:订单支付中的支付方式是不稳定的,刚开始有网银,快捷,以后又有了微信,支付宝。在设计时能够抽象出支付接口,当增长微信时,增长微信实现便可。

 

三、SOLID原则

3.1 单一职责原则(Single Responsibility Principle - SRP)

一个类只有一个引发变化的缘由。若是一个类有多个引发变化的缘由,当其中一个变化时会影响到其余代码。这样代码的内聚性很差,会致使维护性变差,复用性下降。

用于指导对类的设计,只有一个引发变化的缘由,单一职责,设计出高内聚的类(或方法等元素)。

3.2 开放封闭原则(Open Closed Principle - OCP)

对于软件实体应该对扩展开放,对修改关闭。对扩展开放,是指当有新需求或需求变化时,能够仅对代码进行扩展,就能够适应新的需求。对修改关闭是指,类或方法一旦设计完成,就不须要对其进行修改。

实现开闭原则的基础时,找到变化,封装变化。

用于指导可扩展的设计。

3.3 里氏替换原则(Liskov Substitution Principle - LSP)

一个软件实体若是使用的是基类的话, 那么也必定适用于其子类, 并且它根本觉察不错使用的是基类对象仍是子类对象;反过来的代换这是不成立的,

用于指导继承体系的设计。子类出现的子类,父类能够替换,在子类设计时能够扩展父类的功能,但不能改变父类原有的功能。

3.4 最少知识原则(Least Knowledge Principle - LKP)

最少知识原则又叫迪米特法则。一个实体应当尽可能少的与其余实体之间发生相互做用,使得系统功能模块相对独立。也就是说一个软件实体应当尽量少的与其余实体发生相互做用。当一个模块修改时,尽可能少的影响其余的模块,容易扩展,这是对软件实体之间通讯的限制,要求限制软件实体之间通讯的宽度和深度。

用于指导类之间的关系(通讯)设计。

3.5 接口隔离原则(Interface Segregation Principle - ISP)

接口隔离原则的含义是:创建单一接口,不要创建庞大臃肿的接口,尽可能细化接口,接口中的方法尽可能少。

用于指导接口的设计,对接口进行约束。

(1)接口尽可能单一,但要适度,避免过多的接口类定义。

(2)实现类只实现须要的接口便可,当一个类实现多个接口时,调用时在具体场景只使用单一接口便可,把没必要要的隐藏起来。这样依赖关系是最小的。有利于控制变化。

3.6 依赖倒置原则(Dependence Inversion Principle - DIP)

依赖倒置原则的核心思想是面向接口编程,不该该面向实现类编程。

(1)抽象不该该依赖于细节。细节应该依赖于抽象。

(2)高层不该该依赖于底层,二者都应该依赖于抽象。

用于指导抽象设计,依赖稳定的,将稳定的进行抽象。

 

四、其余设计原则

4.1 组合/聚合复用原则(Composition/Aggregation Reuse Principle - CARP)

在设计中,优先考虑使用组合,而不是继承。继承容易产生反作用,组合具备更好的灵活性。如:代理模式、装饰模式、适配器模式等。

4.2 无环依赖原则(Acyclic Dependencies Principle - ADP)

当 A 模块依赖于 B 模块,B 模块依赖于 C 模块,C 依赖于 A 模块,此时将出现循环依赖。在设计中应该避免这个问题,可经过引入“中介者模式”解决该问题。

4.3 共同封装原则(Common Closure Principle - CCP)

将易变的类放在同一个包里,将变化隔离出来。该原则是“开放-封闭原则”的延生。

4.4 共同重用原则(Common Reuse Principle - CRP)

若是重用了包中的一个类,那么也就至关于重用了包中的全部类,咱们要尽量减少包的大小。

4.5 好莱坞原则(Hollywood Principle - HP)

好莱坞明星的经纪人通常都很忙,他们不想被打扰,每每会说:Don't call me, I'll call you. 翻译为:不要联系我,我会联系你。对应于软件设计而言,最著名的就是“控制反转”(或称为“依赖注入”),咱们不须要在代码中主动的建立对象,而是由容器帮咱们来建立并管理这些对象。

4.6 保持它简单与傻瓜(Keep it simple and stupid - KISS)

不要让系统变得复杂,界面简洁,功能实用,操做方便,要让它足够的简单,足够的傻瓜。

4.7 惯例优于配置(Convention over Configuration - COC)

尽可能让惯例来减小配置,这样才能提升开发效率,尽可能作到“零配置”。不少开发框架都是这样作的。

4.8 命令查询分离(Command Query Separation - CQS)

在定义接口时,要作到哪些是命令,哪些是查询,要将它们分离,而不要揉到一块儿。在读写分离或分布式系统中应用较多。

4.9 关注点分离(Separation of Concerns - SOC)

将一个复杂的问题分解为多个简单的问题,而后逐个解决简单的问题,那么复杂的问题就解决了。

4.10 契约式设计(Design by Contract - DBC)

模块或系统之间的交互,都是基于契约(接口或抽象)的,而不要依赖于具体实现。该原则建议咱们要面向契约编程。

 

小结

本文介绍了经常使用的设计原则,基于这些原则,能够用于指导代码设计,架构评审,Code Review等。在设计原则的基础上产生了设计模式,下一篇,咱们会总体介绍GOF 23种设计模式。

相关文章
相关标签/搜索