文档章节

【白话设计模式二十五】临别总结:设计模式与7大原则

陶邦仁
 陶邦仁
发布于 2016/04/05 17:53
字数 4875
阅读 556
收藏 20

#0 系列目录#

#1 创建型模式#

  1. 简单工厂模式(Simple factory pattern): 提供一个创建对象实例的功能,而无需关心具体的实现。被创建实例的类可以是接口、抽象类,也可以是具体的类。

  2. 工厂方法模式(Factory method pattern): 定义了一个创建对象的接口, 但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类。

  3. 抽象工厂模式(Abstract factory pattern): 提供一个接口, 用于创建相关或依赖对象的家族, 而不需要指定具体类

  4. 生成器模式(Builder pattern): 使用生成器模式封装一个产品的构造过程, 并允许按步骤构造。将一个复杂对象的构建与它的表示分离, 使得同样的构建过程可以创建不同的表示。

  5. 原型模式(Prototype pattern): 当创建给定类的实例过程很昂贵或很复杂时, 就使用原形模式。

  6. 单例模式(Singleton pattern): 确保一个类只有一个实例, 并提供全局访问点。

#2 结构型模式#

  1. 适配器模式(Adapter pattern): 将一个类的接口, 转换成客户期望的另一个接口。适配器让原本接口不兼容的类可以合作无间。对象适配器使用组合, 类适配器使用多重继承。

  2. 桥接模式(Bridge pattern): 使用桥接模式通过将实现和抽象放在两个不同的类层次中而使它们可以独立改变

  3. 组合模式(Composite pattern): 允许你将对象组合成树形结构来表现"整体/部分"层次结构。组合能让客户以一致的方式处理个别对象以及对象组合

  4. 装饰者模式(Decorator pattern): 动态地将责任附加到对象上, 若要扩展功能, 装饰者提供了比继承更有弹性的替代方案。

  5. 外观模式(Facade pattern): 提供了一个统一的接口, 用来访问子系统中的一群接口。外观定义了一个高层接口, 让子系统更容易使用。

  6. 亨元模式(Flyweight Pattern): 如想让某个类的一个实例能用来提供许多"虚拟实例", 就使用蝇量模式。

  7. 代理模式(Proxy pattern): 为另一个对象提供一个替身或占位符以控制对这个对象的访问

#3 行为型模式#

  1. 责任链模式(Chain of responsibility pattern): 通过责任链模式, 你可以为某个请求创建一个对象链。每个对象依序检查此请求并对其进行处理或者将它传给链中的下一个对象。

  2. 命令模式(Command pattern): 将"请求"封闭成对象, 以便使用不同的请求,队列或者日志来参数化其他对象. 命令模式也支持可撤销的操作。

  3. 解释器模式(Interpreter pattern): 使用解释器模式为语言创建解释器

  4. 迭代器模式(Iterator pattern): 提供一种方法顺序访问一个聚合对象中的各个元素, 而又不暴露其内部的表示。

  5. 中介者模式(Mediator pattern) : 使用中介者模式来集中相关对象之间复杂的沟通和控制方式。

  6. 备忘录模式(Memento pattern): 当你需要让对象返回之前的状态时(例如, 你的用户请求"撤销"), 你使用备忘录模式。

  7. 观察者模式(Observer pattern): 在对象之间定义一对多的依赖, 这样一来, 当一个对象改变状态, 依赖它的对象都会收到通知, 并自动更新

  8. 状态模式(State pattern): 允许对象在内部状态改变时改变它的行为, 对象看起来好象改了它的类。

  9. 策略模式(Strategy pattern): 定义了算法族, 分别封闭起来, 让它们之间可以互相替换, 此模式让算法的变化独立于使用算法的客户。

  10. 模板方法模式(Template pattern): 在一个方法中定义一个算法的骨架, 而将一些步骤延迟到子类中. 模板方法使得子类可以在不改变算法结构的情况下, 重新定义算法中的某些步骤。

  11. 访问者模式(Visitor pattern): 当你想要为一个对象的组合增加新的能力, 且封装并不重要时, 就使用访问者模式

#4 设计模式和设计原则# ##4.1 设计模式和设计原则的关系## 面向对象的分析设计有很多原则,这些原则大都从思想层面,给我们指出了面向对象分析设计的正确方向,是我们进行面向对象分析设计应该尽力遵守的准则。

而设计模式已经是针对某个场景下某些问题的某个解决方案。也就是说这些设计原则是思想上的指导,而设计模式是实现上的手段,因此设计模式也是应该遵守这些原则的,换句话说,设计模式就是这些设计原则的一些具体体现

##4.2 为何不重点讲设计原则## 既然设计模式是这些设计原则的具体体现,那也就意味着设计模式的思想上的根就是这些设计原则了,没错,可以这么认为。这样一来,有些朋友就会很疑惑了,那么为何不重点讲讲设计原则呢?对于这个问题,我们有如下的考虑:

设计原则本身是从思想层面上进行指导,本身是高度概括和原则性的,只是一个设计上的大体方向,其具体实现并不是只有设计模式这一种。理论上来说,可以在相同的原则指导下,做出很多不同的实现来。

每一种设计模式并不是单一的体现某一个设计原则,事实上,很多设计模式都是融合了很多个设计原则的思想,并不好特别强调设计模式对某个或者是某些设计原则的体现。而且每个设计模式在应用的时候也会有很多的考量,不同使用场景下,突出体现的设计原则也可能是不一样的。

这些设计原则只是一个建议指导,事实上,在实际开发中,很少做到完全遵守,总是在有意无意的违反一些或者是部分设计原则。设计工作本来就是一个不断权衡的工作,有句话说得很好:“设计是一种危险的平衡艺术”,设计原则只是一个指导,有些时候,还要综合考虑业务功能、实现的难度、系统性能、时间与空间等很多方面的问题。

设计模式本身已经很复杂了,已经很难再去深入的探讨这些设计原则,这样也避免出现过多的重点内容,导致大家无所适从。

#5 常见的面向对象设计原则# ##5.1 单一职责原则SRP(Single Responsibility Principle)## 所谓单一职责原则,指的就是:一个类应该仅有一个引起它变化的原因。

这里变化的原因就是所说的“职责”,如果一个类有多个引起它变化的原因,那么也就意味着这个类有多个职责,再进一步说,就是把多个职责耦合在一起了。

这会造成职责的相互影响,可能一个职责的变化,会影响到其它职责的实现,甚至引起其它职责跟着变化,这种设计是很脆弱的。

这个原则看起来是最简单和最好理解的,但是实际上是很难完全做到的,难点在于如何区分这个“职责”,这是个没有标准量化的东西,哪些算职责,到底这个职责有多大的粒度,这个职责如何细化等等。因此,在实际开发中,这个原则也是最容易违反的。

##5.2 开放-关闭原则OCP(Open-Closed Principle)## 所谓开放-关闭原则,指的就是:一个类应该对扩展开放,对修改关闭。一般也被简称为开闭原则,开闭原则是设计中非常核心的一个原则。

开闭原则要求的是,类的行为是可以扩展的,而且是在不修改已有的代码的情况下进行扩展,也不必改动已有的源代码或者二进制代码。

看起来好像是矛盾的,怎么样才能实现呢?

实现开闭原则的关键就在于合理的抽象,分离出变化与不变化的部分,为变化的部分预留下可扩展的方式,比如:钩子方法、或是动态组合对象等等。

这个原则看起来也很简单,但事实上,一个系统要全部做到遵守开闭原则,几乎是不可能的,也没这个必要。适度的抽象可以提高系统的灵活性,使其可扩展、可维护,但是过度的抽象,会大大增加系统的复杂程度。应该在需要改变的地方应用开闭原则就可以了,而不用到处使用,从而陷入过度设计。

##5.3 里氏替换原则LSP(Liskov Substitution Principle)## 所谓里氏替换原则,指的就是:子类型必须能够替换掉它们的父类型。这很明显是一种多态的使用情况,它可以避免在多态的应用中,出现某些隐蔽的错误。

事实上,当一个类继承了另外一个类,那么子类就拥有了父类中可以继承下来的属性和操作,理论上来说,此时使用子类型去替换掉父类型,应该不会引起原来使用父类型的程序出现错误。

但是,很不幸的是,在某些情况下是会出现问题的,比如:如果子类型覆盖了父类型的某些方法,或者是子类型修改了父类型某些属性的值,那么原来使用父类型的程序就可能会出现错误,因为在运行期间,从表面上看,它调用的是父类型的方法,需要的是父类型方法实现的功能,但是实际运行调用的确是子类型覆盖实现的方法,而该方法跟父类型的方法并不一样,那就会导致错误的产生

从另外一个角度来说,里氏替换原则是实现开闭的主要原则之一,开闭原则要求对扩展开放,扩展的一个实现手段就是使用继承,而里氏替换原则是保证子类型能够正确替换父类型,只有能正确替换,才能实现扩展,否则扩展了也会出现错误

##5.4 依赖倒置原则DIP(Dependence Inversion Principle)## 所谓依赖倒置原则,指的就是:要依赖于抽象,不要依赖于具体类。要做到依赖倒置,典型的应该做到:

高层模块不应该依赖于底层模块,二者都应该依赖于抽象;

抽象不应该依赖于具体实现,具体实现应该依赖于抽象;

很多人觉得,层次化调用的时候,应该是高层调用“底层所拥有的接口”,这是一种典型的误解。事实上,一般高层模块包含对业务功能的处理和业务策略选择,应该被重用,应该是高层模块去影响底层的具体实现

因此,这个底层的接口,应该是由高层提出的,然后由底层实现的,也就是说底层的接口的所有权在高层模块,因此是一种所有权的倒置

倒置接口所有权,这就是著名的Hollywood(好莱坞)原则:不要找我们,我们会联系你

##5.5 接口隔离原则ISP(Interface Segregation Principle)## 所谓接口隔离原则,指的就是:不应该强迫客户依赖于他们不用的方法。

这个原则用来处理那些比较“庞大”的接口,这种接口通常会有较多的操作声明,涉及到很多的职责。客户在使用这样的接口的时候,通常会有很多它不需要的方法,这些方法对于客户来讲,就是一种接口污染,相当于强迫用户在一大堆“垃圾方法”里面去寻找他需要的方法

因此,这样的接口应该被分离,应该按照不同的客户需要来分离成为针对客户的接口,这样的接口里面,只包含客户需要的操作声明,这样既方便了客户的使用,也可以避免因误用接口而导致的错误。

分离接口的方式,除了直接进行代码分离之外,还可以使用委托来分离接口,在能够支持多重继承的语言里面,还可以采用多重继承的方式进行分离。

##5.6 最少知识原则(Least Knowledge Principle)## 所谓最少知识原则,指的就是:只和你的朋友谈话。

这个原则用来指导我们在设计系统的时候,应该尽量减少对象之间的交互,对象只和自己的朋友谈话,也就是只和自己的朋友交互,从而松散类之间的耦合。通过松散类之间的耦合来降低类之间的相互依赖,这样在修改系统的某一个部分时候,就不会影响其它的部分,从而使得系统具有更好的可维护性。

那么究竟哪些对象才能被当作朋友呢?最少知识原则提供了一些指导:

当前对象本身;

通过方法的参数传递进来的对象;

当前对象所创建的对象;

当前对象的实例变量所引用的对象;

方法内所创建或实例化的对象;

总之,最少知识原则要求我们的方法调用,必须保持在一定的界限范围之内,尽量减少对象的依赖关系

##5.7 其它原则## 除了上面提到的这些原则,还有一些大家都熟知的原则,比如:

面向接口编程;

优先使用组合/聚合,而非继承;

当然也还有很多大家不是很熟悉的原则,比如:

一个类需要的数据应该隐藏在类的内部;

类之间应该零耦合,或者只有传导耦合,换句话说,类之间要么没有关系,要么只使用另一个类的接口提供的操作;

在水平方向上尽可能统一的分布系统功能;

#6 设计原则总结#

  1. 单一职责原则【SINGLE RESPONSIBILITY PRINCIPLE】:一个类负责一项职责。

  2. 里氏替换原则【LISKOV SUBSTITUTION PRINCIPLE】:继承与派生的规则。

  3. 依赖倒置原则【DEPENDENCE INVERSION PRINCIPLE】:高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。即针对接口编程,不要针对实现编程。

  4. 接口隔离原则【INTERFACE SEGREGATION PRINCIPLE】:建立单一接口,不要建立庞大臃肿的接口,尽量细化接口,接口中的方法尽量少。

  5. 迪米特法则【LOW OF DEMETER】:低耦合,高内聚。

  6. 开闭原则【OPEN CLOSE PRINCIPLE】:一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。

  7. 组合/聚合复用原则【Composition/Aggregation Reuse Principle(CARP)】:尽量使用组合和聚合少使用继承的关系来达到复用的原则。

#7 不是结束而是新的开始# 到此,设计模式系列已经讲完结束了,但恰恰相反,要想在设计上更进一步的话,困难才刚刚开始。更多的是需要思考和领悟,其难度比从入门到中级要大得多。

因此对你而言,看完本系列并不是学习的结束,而是新的开始。

##7.1 你该怎么做## 如果已经深入领会和掌握了本书的内容,还想要在设计模式上继续精进的朋友,给出如下的建议:

  1. 多看

多搜寻一些应用设计模式的实际项目、工程或是框架,参考别人的成功应用,再结合自己的经验来思考和使用。当然项目不应该太大,太大了很难完全看懂;也不能太小,太小了,没有太大实用价值,尤其是无法参考多个模式综合应用的情况,帮助就不大了。

  1. 多练

多寻找机会,把这些设计模式在实际应用中使用,只有亲自动手去试验和使用,才能真正掌握和领会设计模式的精髓。

  1. 多总结

认真分析每次对设计模式的使用是否得当,有什么经验和教训,是否有变形使用的情况,在不断总结中进步。

  1. 反复参阅本系列

理论联系实际,通过实际应用反过来加深对理论的理解,以达到融会贯通这些设计模式的知识。因此,你需要反复参阅本系列,然后实践,然后再回头看系列上的知识,你会有不一样的体会和领悟。

  1. 多思考

多从设计上去思考这些设计模式,考虑它的设计意图、设计思想、解决问题的方式、实现的原理、模式的本质、以及如何变形使用等等。

© 著作权归作者所有

陶邦仁
粉丝 1688
博文 420
码字总数 1483887
作品 0
海淀
技术主管
私信 提问
(目录)设计模式(可复用面向对象软件的基础)

本系列“设计模式”博客使用Golang语言实现算法。所谓算法是指解决一个问题的步骤,个人觉得不在于语言。小弟只是最近学习Golang,所以顺带熟练一下语法知识,别无它意。 本系列博客主要介绍...

chapin
2015/01/13
158
0
《JavaScript设计模式与开发实践》原则篇(2)—— 最少知识原则

最少知识原则(LKP)说的是一个软件实体应当尽可能少地与其他实体发生相互作用。这 里的软件实体是一个广义的概念,不仅包括对象,还包括系统、类、模块、函数、变量等。 单一职责原则指导我们...

嗨呀豆豆呢
2018/12/30
0
0
PHP设计模式(一):简介及创建型模式

我们分三篇文章来总结一下设计模式在PHP中的应用,这是第一篇创建型模式。 一、设计模式简介 首先我们来认识一下什么是设计模式: 设计模式是一套被反复使用、容易被他人理解的、可靠的代码设...

juhenj
2014/05/15
285
2
设计模式 2014-12-19

book: 阎宏《JAVA与模式》 架构设计栏目 http://blog.csdn.net/enterprise/column.html 概要: http://bbs.csdn.net/forums/Embeddeddriver 23种设计模式分别是: 1.单例模式 2.工厂方法模式...

jayronwang
2014/12/19
293
0
JavaScript设计模式总结

之前看过《JavaScript设计模式与开发实践》这本书,对书中的设计模式和一些相关案例也有了一定的了解,同时把这些设计模式的应用对应在在一些其他的项目中,进行了一些整理,如下仅供参考: ...

jefferyE
03/26
0
0

没有更多内容

加载失败,请刷新页面

加载更多

关于运维,该怎么决定它的方向,这个似工作又似兴趣的存在

我之前主要从事网络、桌面、机房管理等相关工作,这些工作使我迷惘,这应该是大多数运维人都经历过的过程; 18年国庆,我从国内前三的消费金融公司裸辞,下海创业,就是想要摆脱这样的困境。...

网络小虾米
22分钟前
5
0
Java Timer的用法

Timer timer = new Timer(); timer.schedule(new TimerTask() { public void run() { System.out.println("11232"); } }, 200000 , 1000); public void schedule(TimerTask task, long delay......

林词
26分钟前
5
0
使用js动态加载外部js文件以及动态创建script脚本

动态脚本指的是在页面加载时不存在,但将来的某一时刻通过修改该DOM动态添加的脚本。和操作HTML元素一样,创建动态脚本也有两种方式:插入外部文件和直接插入JavaScript代码。 动态加载外的外...

Bing309
34分钟前
3
0
从零开始入门 K8s | Kubernetes 网络概念及策略控制

作者 | 阿里巴巴高级技术专家 叶磊 一、Kubernetes 基本网络模型 本文来介绍一下 Kubernetes 对网络模型的一些想法。大家知道 Kubernetes 对于网络具体实现方案,没有什么限制,也没有给出特...

阿里巴巴云原生
38分钟前
3
0
天气获取

本文转载于:专业的前端网站➨天气获取 $.get("http://wthrcdn.etouch.cn/WeatherApi", { citykey: cityCode }, function (d) { //创建文档对象 var parser = new ......

前端老手
38分钟前
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部