[设计模式]之零:六大设计原则

设计模式系列目录

单一职责原则 Single Responsibility Principle - SRP

就一个类而言,应该仅有一个引起它变化的原因。

假设现在要在iPhone上做一个图片编辑工具。功能有裁剪图片,旋转图片,缩放移动照片等等。

呐,我们可以写一个功能集类,然后把这些所有操作视为功能集的一部分,把代码全部写进这个类里面。

这么看来似乎可以,因为这是作为一个单独的模块嘛,把相关功能写进一个工具类里,用哪个功能调用哪个函数就好了。但这带来了一个问题就是这个工具类包含过多功能显得非常臃肿,不容易维护。而且在一个类里往往容易出现几个函数共用一个全局变量的情况,功能之间耦合度太大,难以复用。

举个最直接的例子:如果我想把这个功能移植到Android上去怎么办。这个移植过程麻烦之处并不在于语言语法变化,而是两个系统有着完全不同的手势传递机制,我要用手旋转,缩放图片这段代码完全没法复用,唯一能用的裁剪代码,也可能因为和其他代码耦合过大导致需要重新修改,退一步说,裁剪算法就算没有耦合,代码可以直接用,但关系到手势的代码对我来说都成为冗余代码,这对于代码复用就是灾难。

如果一个类承担的职责过多,就等于把这些职责偶合在一起,一个职责的变化可能会削弱或者抑制这个类完成其他职责的能力。这种耦合会导致脆弱的设计,当变化发生时,设计会遭受到意想不到的破坏。

所以这里在设计的时候,就要考虑一下把这些功能分类。比如裁剪功能需要知道裁剪框大小,位置。那就分离出一个类,专门负责计算裁剪框四个点的坐标变化。旋转缩放图片需要知道图片的大小,缩放率,显示方向等信息,那就再分离出一个类,负责计算图片形态的变化。最后剩下手势再封装一个类,处理手势的逻辑,在不同情况下获取不同的手势数据,作为参数交给上面两个算法类进行计算输出。

这样一来,每个类的职责就变得单一了,维护就容易多了。后面再移植代码的话,算法类只需要切换语法,手势类只要去重写触发手势的条件,而不必修改逻辑。代码很快就可以改好,并且不会破坏原有的项目结构。

软件设计真正要做的许多内容,就是发现职责并把那些职责相互分离。判断是否要分离出类的方法就是,如果你能够想到多于一个的动机去改变一个类,那么这个类就具有多于一个的职责,就应该考虑类的职责分离。

优点

  • 可以降低类的复杂度,一个类只负责一项职责,其逻辑肯定要比负责多项职责简单的多
  • 提高类的可读性,提高系统的可维护性
  • 变更引起的风险降低,变更是必然的,如果单一职责原则遵守的好,当修改一个功能时,可以显著降低对其他功能的影响

里氏替换原则 Liskov Substitution Principle - LSP

子类型必须能够替换掉他们的父类型

通俗的讲,一个软件实体如果使用的是一个父类的话,那么一定适用于其子类,而且它察觉不出父类对象和子类对象的区别。即,在软件里,把父类都替换成它的子类,程序的行为没有变化。

里氏替换原则的重点在不影响原功能,而不是不覆盖原方法。

所以正常遵从该原则的处理办法是在需要覆盖父类方法时应该首先考虑使用super调用父类的同名方法以保证父类同名方法会被调用。

如果确实不需要调用父类方法,则不加此语句。

这个原则很重要,编码时要注意。


依赖倒置原则 Dependence Inversion Principle - DIP

抽象不应该依赖细节,细节应该依赖抽象

通俗的说,就是要针对接口编程,不要对实现编程。呐,比如说电脑主板,CPU,内存,硬盘这些硬件的设计就是依赖接口设计的。单拿CPU来说,CPU有各种厂家设计的各种型号,这些型号的内部设计实现都不相同,但他们的接口是一样的,这样主板就可以随意更换CPU了。

关于倒置,比如说我有一个高层模块,模块实现对SQLite读写的功能依赖一个控制访问SQLite的低层模块。一旦我要求把SQLite改为MySQL,那这个低层模块就无法正常工作,进而倒置上层模块也无法正常工作。依赖倒置就是说设计代码不再是上层依赖下层,而是两层都去依赖接口去实现,这样两层的运行状态便不会互相影响。

依赖倒转其实可以说是面向对象设计的标志,用哪种语言来编写程序不重要,如果编写时考虑的都是如何针对抽象编程而不是针对细节编程,即程序中所有的依赖关系都是终止于抽象类或者接口,那就是面向对象的设计,反之那就是过程化的设计了。

依赖倒置原则的实现可以参考策略模式:设计模式之二:策略模式
例子中的收取现金的不同方式可以看做CPU的不同型号。调用收现金的方法可看做主板插上不同型号的CPU。就是这么个思想。

遵循依赖倒置原则可以降低类之间的耦合性,提高系统的稳定性,降低修改程序造成的风险。

根据该原则,编程中要注意

  • 低层模块尽量都要有抽象类或接口,或者两者都有
  • 变量的声明类型尽量是抽象类或接口
  • 使用继承时遵循里氏替换原则

接口隔离原则 Interface Segregation Principle - ISP

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

看图,图一是未遵循该原则的结构:

接口隔离原则的含义是:建立单一接口,不要建立庞大臃肿的接口,尽量细化接口,接口中的方法尽量少。也就是说,我们要为各个类建立专用的接口,而不要试图去建立一个很庞大的接口供所有依赖它的类去调用。

接口是设计时对外部设定的“契约”,通过分散定义多个接口,可以预防外来变更的扩散,提高系统的灵活性和可维护性。

注意事项

  • 接口尽量小,但是要有限度。对接口进行细化可以提高程序设计灵活性是不挣的事实,但是如果过小,则会造成接口数量过多,使设计复杂化。所以一定要适度
  • 为依赖接口的类定制服务,只暴露给调用的类它需要的方法,它不需要的方法则隐藏起来。只有专注地为一个模块提供定制服务,才能建立最小的依赖关系
  • 提高内聚,减少对外交互。使接口用最少的方法去完成最多的事情

迪米特法则 Law Of Demeter - LOD

一个对象应该对其他对象保持最少的了解。

如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用。如果其中一个类需要调用另一个类的某一个方法,可以通过第三者转发这个调用。

迪米特法则首先强调的前提是在类的结构设计上,每一个类应当尽量降低成员的访问权限,也就是要降低类之间的耦合。类之间的耦合越弱,越有利于复用,修改类相互之间的影响也会降到最低。

迪米特法则还有一个更简单的定义:只与直接的朋友通信。首先来解释一下什么是直接的朋友:每个对象都会与其他对象有耦合关系,只要两个对象之间有耦合关系,我们就说这两个对象之间是朋友关系。耦合的方式很多,依赖、关联、组合、聚合等。其中,我们称出现成员变量、方法参数、方法返回值中的类为直接的朋友,而出现在局部变量中的类则不是直接的朋友。也就是说,陌生的类最好不要作为局部变量的形式出现在类的内部。

迪米特法则的初衷是降低类之间的耦合,由于每个类都减少了不必要的依赖,因此的确可以降低耦合关系。但是凡事都有度,虽然可以避免与非直接的类通信,但是要通信,必然会通过一个“中介”来发生联系,例如本例中,总公司就是通过分公司这个“中介”来与分公司的员工发生联系的。过分的使用迪米特原则,会产生大量这样的中介和传递类,导致系统复杂度变大。所以在采用迪米特法则时要反复权衡,既做到结构清晰,又要高内聚低耦合。


开闭原则 Open Close Principle - OCP

软件实体(类,模块,函数等)应该可以拓展,但是不可修改

这个原则有两点:

  • 对于拓展是开放的 Open for extension
  • 对于更改是封闭的 Closed for modification

在软件的生命周期内,因为变化、升级和维护等原因需要对软件原有代码进行修改时,可能会给旧代码中引入错误,也可能会使我们不得不对整个功能进行重构,并且需要原有代码经过重新测试。所以当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化。

但在设计软件的时候,无论模块是多么的封闭,都会存在无法对之封闭的变化,因为你不可能在编码前就考虑到所有情况。所以在设计代码时就必须先猜测出最可能发生变化的种类,然后构造抽象来隔离变化。在编码之后,一旦遇到发生变化的地方,那就应该首先考虑要不要对这里进行结构的修改。也就是遇到变化发生时要立即采取行动

比如现在在客户端类中写了一个加法程序,后来说要增加减法,那么这时就应该立即抽象出来一个运算类。虽然说直接在客户端增加减法算法很快,但考虑到以后也许会拓展更多的算法,而且代码改得越晚修改代码的范围就越大。立即修改代码结构的代价似乎比以后去改的代价要小很多。

我们希望的是在开发工作展开不久就知道可能发生的变化。查明可能发生的变化所等待的时间越长,要创建正确的抽象就越困难
开放-封闭原则是面向对象设计的核心所在。遵循这个原则可以带来面向对象技术所声称的巨大好处,也就是可维护,可拓展,可复用,灵活性好。开发人员应该对程序中呈现出频繁变化的那些部分作出抽象,然而,对于应用程序中的每个部分都刻意地进行抽象同样不是一个好主意。拒绝不成熟的抽象和抽象本身一样重要。


参考
http://blog.csdn.net/zhengzhb/article/category/926691/

原文:大专栏  [设计模式]之零:六大设计原则

原文地址:https://www.cnblogs.com/sanxiandoupi/p/11632196.html

时间: 2024-10-12 19:59:05

[设计模式]之零:六大设计原则的相关文章

设计模式中的六大设计原则之三,四

求二叉树的宽度和深度 给定一个二叉树,获取该二叉树的宽度和深度. 例如输入 a / \ b c / \ / \ d e f g 返回3. 详细描述: 接口说明 原型: int GetBiNodeInfo(BiNode &head, unsigned int *pulWidth, unsigned int *pulHeight) 输入参数: head 需要获取深度的二叉树头结点 输出参数(指针指向的内存区域保证有效): pulWidth 宽度 pulHeight 高度 返回值: 0 成功 1 失败

设计模式中的六大设计原则之一,二

最近在学习设计模式方面的知识,首先接触到的是设计模式中的六大设计原则: 1.单一职责原则: 2.里氏替换原则:3.依赖倒置原则:4.接口隔离原则:5.迪米特法则:开闭原则.下面我来讲讲我对这六大设计自己的理解,如有欠缺地地方,请大家及时指出啊...   1.单一职责原则:应该有且仅有一个原因引起类的变更.通俗的说,即一个类只负责一项职责.下面我们举一个具体的例子来说明一下什么是单一职责原则.电话通话的时候有4个过程发生:拨号,通话,回应,挂机,首先看下面这样一个借口,如图1所示: 图1. 我们来

了解设计模式先从六大设计原则说起

了解设计模式的朋友们,想必都听说过"六大设计原则"吧.其实最经典的 23 种设计模式中或多或少地都在使用这些设计原则,也就是说,设计模式是站在设计原则的基础之上的.所以在学习设计模式之前,很有必要对这些设计原则先做一下了解. GoF(四人帮),传说中的四位大神们,他们联手搞出了一套设计模式,堪称 OOD(面向对象设计)的经典之作!震惊了整个软件开发领域.但这四个老家伙非常怪异,总是喜欢显摆一些高深的理论,甚至有时候不说人话,十分让人费解. 除了最经典的六大设计原则以外,还有一些其他的设

JAVA设计模式总结之六大设计原则

从今年的七月份开始学习设计模式到9月底,设计模式全部学完了,在学习期间,总共过了两篇:第一篇看完设计模式后,感觉只是脑子里面有印象但无法言语.于是决定在看一篇,到9月份第二篇设计模式总于看完了,这一篇看完,脑子里面已经能够对绝大多数的设计模式能够说出其核心思想且可以画出类图也知道应用场景,算是一个进步,但可能还不能够特别熟练的使用,可能需要多多巩固和强化使用才能够完全理解设计模式的精髓所在.学习期间收获还是不少的: 1.从只听过设计模式到学习了所有的设计模式,并写了不少设计模式的博客,在公司期间

【设计模式】 面向对象六大设计原则

面向对象设计的六大原则 : 单一职责原则, 里氏替换原则, 依赖倒置原则, 接口隔离原则, 迪米特法则, 开闭原则; 一. 单一职责原则 1. 单一职责简介 单一职责定义 : 有且只有一个原因引起类的变化, 一个接口 或者 类 只有一个职责; 单一职责的好处 : -- 复杂性 : 降低类的复杂性, 对类或接口的职责有清晰明确定义; -- 可读性 : 提高可读性; -- 维护 : 提高可维护性; -- 变更风险 : 降低变更引起的风险, 接口改变只影响相应的实现类, 不影响其他类; 2. 单一职责

设计模式之禅--六大设计原则之接口隔离原则

设计模式就是让我们更方便的解决问题. 这里分享一个故事.我有一个朋友,嗯没错就是一个朋友,参加一个软件比赛,一个同学写服务器上的代码,三天两头更新,丝毫不考虑写客户端的人的感受,简直不能再牛.如果Java的更新有这么一次,没有考虑在不影响以前代码的基础上做修改,得有多少程序员吐血身亡. 接口隔离原则的定义: 建立单一接口,不要建立臃肿放大的接口.接口尽量细化,同时接口中的方法尽量少. 这不是单一职责原则,单一职责要求的是类和接口的职责单一,注重的是职责,这是业务逻辑上的划分,而借口隔离原则要求接

设计模式之禅--六大设计原则之迪米特原则

定义: 一个对象应该对其他对象有最少的了解 它包含以下四层定义 只和朋友交流 朋友类的定义是这样的:出现在成员变量.方法的输入输出函数中的类成为成员朋友类,而出现在方法体内部的类不属于朋友类. 我吧书上的例子简化了一下: A让B去数有多少个C A让B去数有多少个C public class A{ public void command(B b){ List<C> lists = new List<C>(); for(int i = 0; i < 10; ++i){ list.

设计模式之六大设计原则

在上篇博文中提到了开放-封闭原则,没有细谈,这次我们来总结一下设计模式的几大原则. 1开放-封闭原则:是指软件实体(类.模块.函数等)应该可以扩展,但是不可修改. 对原则的理解:开闭原则是最具有理想主义色彩的一个原则,它是面向对象设计的终极目标,下面所要介绍的几个原则可以看成是为了符合开闭原则所作的努力和解决办法.对于开闭原则通俗的理解就是,能不改就不改,能少改尽可能的少改.周所周知,物质是运动的,世界是变化的,想要让一个事物永恒不变是不可能的,所以要想让软件绝对符合开闭原则是不可能的. 2单一

设计模式小结——六大设计原则

设计模式是一套由软件界前辈们总结出的可以反复使用的编程经验,旨在提高代码的可重用性,提高系统的可维护性,以及解决一系列复杂问题.设计模式包括6大设计原则和23种种设计模式.6大设计原则:单一职责原则SRP 应该有却仅有一个原因引起类的变更,即类最好只实现一种功能.高内聚. 单一职责的实现方式是一个职责一个接口. 单一职责适用于类和接口,同样适用于方法,一个方法也应该只做好一件事.里氏替换原则LSP 所有能使用父类的地方必须能透明地使用其子类的对象. 子类必须完全实现父类的方法,如果子类不能完整实