程序员人生 网站导航

设计模式总结:概念、原则、23种模式分析

栏目:综合技术时间:2016-06-25 08:55:58

1、甚么是设计模式

设计模式从本质上讲,是针对过去某种经验的总结。每种设计模式都是为了在特定条件下去解决某种问题。

设计模式中的每个模式描写了1个在我们周围不断重复产生的问题,和该问题的解决方案的核心。这样,你就可以1次又1次地使用该方案而没必要做重复劳动。它是可复用面向对象软件的基础。

设计模式解决的是系统设计问题,设计模式是“术”,设计模式背后的意图才是“道”。GoF提出了23中设计模式,是对平常用到的模式总结归纳出的,他们不是1盘散沙,是有关系的。就是对象的生命周期1步1步的将各个设计模式串连在了1起。对象的生命周期中,会1步1步的遇到总共23种设计问题,所以才会有23种设计模式。

2、设计模式原则:SOLID+CD

2.1 S - 单1职责原则

Single Responsibility Principle, SRP:1个类只能有1个让它变化的缘由,即1个类只承当1个职责。

2.2 O - 开放封闭原则

Open-Close Principle, OCP:我们的设计应当对扩大开放,对修改封闭。即尽可能以扩大的方式来保护系统。如果遇到需求变化,要通过添加新的类来实现,而不是修改现有的代码。

2.3 L - 里氏代换原则

Liskov Subsititution Principle, LSP:子类可以完全覆盖父类。它表示我们可以在代码中使用任意子类来替换父类并且程序不受影响,这样可以保证我们使用“继承”并没有破坏父类。

反过来则不成立,如果1个软件实体使用的是1个子类对象的话,那末它不1定能够使用基类对象。例如:我喜欢动物,那我1定喜欢狗,由于狗是动物的子类;但是我喜欢狗,不能据此判定我喜欢(所有)动物。

2.4 I - 接口隔离原则

Interface Segregation Principle, ISP:每一个接口都实现单1的功能。使用多个专门的接口,而不使用单1的总接口。添加新功能时,要增加1个新接口,而不是修改已有的接口,制止出现“胖接口”。

2.5 D – 依赖颠倒原则

Dependence Inversion Principle, DIP:这里主要是提倡“面向接口”编程,而非“面向实现”编程。具体依赖于抽象,而非抽象依赖于具体。即,要把不同子类的相同功能抽象出来,依赖与这个抽象,而不是依赖于具体的子类。

2.6 迪米特法则

又称最少知道原则(Demeter Principle)。最少知道原则是指:1个实体应当尽可能少地与其他实体之间产生相互作用,使得系统功能模块相对独立。

2.7 合成复用原则

Composite Reuse Principle:尽可能使用合成/聚合的方式,而不是使用继承。

3、设计模式分类

根据模式的目的可分为:创建型、结构型、行动型3种。
1)创建型模式:与对象的创建有关;
2)结构型模式:处理类或对象的组合;
3)行动型模式:对类或对象怎样交互和怎样分配职责进行描写。

3.1 创建型模式(Creational Pattern)

1、 抽象工厂模式(Abstract Factory Pattern)
工厂(Factory )和产品(Product)是Abstract Factory 模式的主要参与者。该模式描写了怎样在不直接实例化类的情况下创建1系列相干的产品对象。

工厂方法模式通过引入工厂等级结构,解决了简单工厂模式中工厂类职责太重的问题,但由于工厂方法模式中的每一个工厂只生产1类产品,可能会致使系统中存在大量的工厂类,必将会增加系统的开消。此时,我们可以斟酌将1些相干的产品组成1个“产品族”,由同1个工厂来统1生产,这就是抽象工厂模式的基本思想。
有时候我们希望1个工厂可以提供多个产品对象,而不是单1的产品对象,如1个电器工厂,它可以生产电视机、电冰箱、空调等多种电器,而不是只生产某1种电器。
这里写图片描述
每个具体工厂可以生产属于1个产品族的所有产品。如果使用工厂方法模式,图4所示结构需要提供15个具体工厂,而使用抽象工厂模式只需要提供5个具体工厂,极大减少了系统中类的个数。

2、 建造者模式(Builder Pattern)
又称生成器模式。将1个复杂对象的构建与它的表示分离,使得一样的构建进程可以创建不同的表示。为客户端返回的不是1个简单的产品,而是1个由多个部件组成的复杂产品。

它将客户端与包括多个组成部份(或部件)的复杂对象的创建进程分离,客户端不必知道复杂对象的内部组成部份与装配方式,只需要知道所需建造者的类型便可。

建造者模式与抽象工厂模式有点类似,但是建造者模式返回1个完全的复杂产品,而抽象工厂模式返回1系列相干的产品;在抽象工厂模式中,客户端通过选择具体工厂来生成所需对象,而在建造者模式中,客户端通过指定具体建造者类型并指点Director类如何去生成对象,侧重于1步步构造1个复杂对象,然后将结果返回。如果将抽象工厂模式看成1个汽车配件生产厂,生成不同类型的汽车配件,那末建造者模式就是1个汽车组装厂,通过对配件进行组装返回1辆完全的汽车。

3、 原型模式(Prototype Pattern)
用原型实例指定创建对象的种类,并且通过拷贝这些原型来创建新的对象。通过1个原型对象克隆出多个1模1样的对象。在使用原型模式时,我们需要首先创建1个原型对象,再通过复制这个原型对象来创建更多同类型的对象。

原型模式的工作原理很简单:将1个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过要求原型对象拷贝自己来实现创建进程。

Prototype有许多和Abstract Factory和B u i l d e r1样的效果:对客户隐藏了具体的产品类,因此减少了客户知道的名字的数目。另外,这些模式使客户无需改变便可使用与特定利用相干的类。

另外1些优点:
1 ) 比其他创建型模式更加灵活,由于客户可以在运行时刻建立和删除原型。
2 ) 克隆1个原型类似于实例化1个类。可以极大的减少系统所需类的数目。
3) 改变结构以指定新对象。
4 ) 减少子类的构造。使得克隆1个原型而不是要求1个工厂方法去产生新的对象。
5) 用类动态配置利用。1些运行时刻环境允许你动态将类装载到利用中。

主要缺点是每个Prototype的子类都必须实现Clone操作,这可能很困难。

4、 工厂方法模式(Factory Method Pattern)
介绍定义1个用于创建对象的接口,让子类决定将哪个类实例化。Factory Method使1个类的实例化延迟到其子类。

工厂方法模式中,1种产品(1个产品类)对应1个工厂,针对不同的产品提供不同的工厂。
这里写图片描述
主要优点:用户只需要关心所需产品对应的工厂,不必关心创建细节,乃至不必知道具体产品类的类名。 主要缺点:在添加新产品时,需要编写新的具体产品类,而且还要提供与之对应的具体工厂类,系统中类的个数将成对增加,在1定程度上增加了系统的复杂度。

5、 单例模式(Singleton Pattern)
保证1个类唯一1个实例,并提供1个访问它的全局访问点。

Singleton模式有许多优点:
1) 对唯1实例的受控访问。由于Singleton类封装它的唯1实例,所以它可以严格的控制客户怎样和什么时候访问它。
2) 缩小名空间。Singleton模式是对全局变量的1种改进。它避免了那些存储唯1实例的全局变量污染名空间。
3) 允许对操作和表示的精化。Singleton类可以有子类,而且用这个扩大类的实例来配置1个利用是很容易的。你可以用你所需要的类的实例在运行时刻配置利用。
4) 允许可变数目的实例。可以用相同的方法来控制利用所使用的实例的数目。

使用Singleton模式所要斟酌的实现问题:
保证1个唯1的实例。做到这1点的1个经常使用方法是将创建这个实例的操作隐藏在1个类操作(即1个静态成员函数或是1个类方法)后面,由它保证只有1个实例被创建。单例模式实现!!!!!

//Singleton类定义以下: class Singleton{ public: //客户只能使用该成员函数访问这个单例 static Singleton* Instance();//静态方法,保证只创建1个实例 protected: //构造函数为保护型,试图直接实例化Singleton将会编译毛病 Singleton();//这就保证了唯一1个实例可以被创建 private: //Singleton类型的指针,将用于指向Singleton的唯1的实例 static Singleton* _instance; }; //相应的实现 Singleton* Singleton::_instance=0;//静态数据成员初始化 Singleton* Singleton::Instance(){ if(_instance==0){//只在首次创建 _instance = new Singleton; } return _intance; }

实例讲授:在Windows系统中屡次点击“启动任务管理器”,看能否打开多个任务管理器窗口?不管我们启动任务管理多少次,Windows系统始终只能弹出1个任务管理器窗口,也就是说在1个Windows系统中,任务管理器存在唯1性。为了确保对象的唯1性,我们可以通过单例模式来实现,这就是单例模式的动机所在。

3.2 结构型模式(Structural Pattern)

6、 适配器模式(Adapter Pattern)
将1个类的接口转换成客户希望的另外1个接口。Adapter模式使得本来由于接口不兼容而不能1起工作的那些类可以1起工作。

适配器模式中引入了1个被称为适配器(Adapter)的包装类,而它所包装的对象称为适配者(Adaptee),即被适配的类。适配器的实现就是把客户类的要求转化为对适配者的相应接口的调用。也就是说:当客户类调用适配器的方法时,在适配器类的内部将调用适配者类的方法,而这个进程对客户类是透明的,客户类其实不直接访问适配者类。

Adapter对Adaptee和Target进行适配,在对象适配器中,它通过继承Target并关联1个Adaptee对象使2者产生联系。

适配器模式的类图:
这里写图片描述
如图客户端需要调用request()方法,而适配者类Adaptee没有该方法,但是它所提供的specificRequest()方法却是客户端所需要的。为了使客户端能够使用适配者类,需要提供1个包装类Adapter,即适配器类。这个包装类包装了1个适配者的实例,从而将客户端与适配者衔接起来,在适配器的request()方法中调用适配者的specificRequest()方法。
对象适配器实现以下:!!!!

#include <iostream> using namespace std; class Target { public: Target(){} virtual ~Target(){} virtual void Request() { cout<<"Target::Request"<<endl; } }; class Adaptee { public: void SpecificRequest() { cout<<"Adaptee::SpecificRequest"<<endl; } }; class Adapter : public Target { public: Adapter() : m_Adaptee(new Adaptee) {} ~Adapter() { if (m_Adaptee != NULL) { delete m_Adaptee; m_Adaptee = NULL; } } void Request() { m_Adaptee->SpecificRequest(); } private: Adaptee *m_Adaptee; }; int main() { Target *targetObj = new Adapter();//基类指针指向子类对象 targetObj->Request();调用适配后的Request()方法 delete targetObj; targetObj = NULL; return 0; }

实例讲授:
我的笔记本电脑的工作电压是20V,而我国的家庭用电是220V,如何让20V的笔记本电脑能够在220V的电压下工作?答案是引入1个电源适配器(AC Adapter),俗称充电器,有了它,生活用电和笔记本电脑便可兼容。

7、 桥接模式(Bridge Pattern)
可以将抽象部份与它的实现部份分离,使它们都可以独立地变化。

Bridge模式的目的就是允许分离的类层次1起工作,即便它们是独立演变的。

如果软件系统中某个类存在两个独立变化的维度,通过该模式可以将这两个维度分离出来,使二者可以独立扩大,让系统更加符合“单1职责原则”

桥接模式用1种奇妙的方式处理多重继承存在的问题,用抽象关联取代了传统的多重继承,将类之间的静态继承关系转换为动态的对象组合关系,使得系统更加灵活,并易于扩大,同时有效控制了系统中类的个数。
使用桥接模式实例:两个维度(毛笔大小和毛笔色彩)分开。针对两个不同的维度提取抽象类和实现类接口,并建立1个抽象关联关系。
这里写图片描述
上图中,如果需要增加1种新型号的毛笔,只需扩大左边的“抽象部份”,增加1个新的扩充抽象类;如果需要增加1种新的色彩,只需扩大右边的“实现部份”,增加1个新的具体实现类。

8、 组合模式(Composite Pattern)
组合模式描写了面向对象递归组合的本质。

将对象组合成树形结构以表示“部份-整体”的层次结构。它使得客户对单个对象和复合对象的使用具有1致性。

组合模式的关键是定义了1个抽象构件类,它既可以代表叶子,又可以代表容器,而客户端针对该抽象构件类进行编程,不必知道它到底表示的是叶子还是容器,可以对其进行统1处理。!!!

9、 装潢模式(Decorator Pattern)
动态地给1个对象添加1些额外的职责。就扩大功能而言,它比生成子类方式更加灵活。

装潢模式可以在不改变1个对象本身功能的基础上给对象增加额外的新行动。对已有对象(新居)的功能进行扩大(装修),以取得更加符适用户需求的对象,使得对象具有更加强大的功能。

10、 外观模式(Facade Pattern)
为子系统中的1组接口提供1个1致的界面,Facade模式定义了1个高层接口,这个接口使得这1子系统更加容易使用。

在软件开发中,有时1个客户类需要和多个业务类交互,特别需要1个类似服务员1样的角色,由它来负责和多个业务类进行交互,而客户类只需与该类交互外观模式通过引入1个新的外观类(Facade)来实现该功能,外观类充当了软件系统中的“服务员”,它为多个业务类的调用提供了1个统1的入口,简化了类与类之间的交互。下降了系统的耦合度。!!!!
这里写图片描述

11、 享元模式(Flyweight Pattern)
应用同享技术有效地支持大量细粒度的对象。

将具有相同内部状态的对象存储在享元池中,享元池中的对象是可以实现同享的,需要的时候就将对象从享元池中取出,实现对象的复用。

避免系统中出现大量相同或类似的对象,同时又不影响客户端程序通过面向对象的方式对这些对象进行操作。享元模式正为解决这1类问题而诞生。享元模式通过同享技术实现相同或类似对象的重用,在逻辑上每个出现的字符都有1个对象与之对应,但是在物理上它们却同享同1个享元对象。

12、 代理模式(Proxy Pattern)
介绍为其他对象提供1个代理以控制对这个对象的访问。

当没法直接访问某个对象或访问某个对象存在困难时可以通过1个代理对象来间接访问,为了保证客户端使用的透明性,所访问的真实对象与代理对象需要实现相同的接口
这里写图片描述
代理模式的共同优点以下:
(1) 能够调和调用者和被调用者,在1定程度上下降了系统的耦合度。
(2) 客户端可以针对抽象主题角色进行编程,增加和更换代理类不必修改源代码,符合开闭原则,系统具有较好的灵活性和可扩大性。

另外,不同类型的代理模式也具有独特的优点,例如:
(1) 远程代理为位于两个不同地址空间对象的访问提供了1种实现机制,可以将1些消耗资源较多的对象和操作移至性能更好的计算机上,提高系统的整体运行效力。
(2) 虚拟代理通过1个消耗资源较少的对象来代表1个消耗资源较多的对象,可以在1定程度上节省系统的运行开消。
(3) 缓冲代理为某1个操作的结果提供临时的缓存存储空间,以便在后续使用中能够同享这些结果,优化系统性能,缩短履行时间。
(4) 保护代理可以控制对1个对象的访问权限,为不同用户提供不同级别的使用权限。
代理模式的主要缺点以下:
(1) 由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成要求的处理速度变慢,例如保护代理。
(2) 实现代理模式需要额外的工作,而且有些代理模式的实现进程较为复杂,例如远程代理。

3.3 行动型模式(Behavioral Pattern)

13、 责任链模式(Chain of Responsibility Pattern)
为消除要求的发送者和接收者之间耦合,而使多个对象都有机会处理这个要求。将这些对象连成1条链,并沿着这条链传递该要求,直到有1个对象处理它。

14、 命令模式(Command Pattern)
该模式描写了怎样封装要求,也描写了1致性的发送要求的接口,允许你配置客户端以处理不同要求。对要求排队或记录要求日志,和支持可取消的操作。

15、 解释器模式(Interpreter Pattern)
给定1个语言,定义它的文法的1种表示,并定义1个解释器, 该解释器使用该表示来解释语言中的句子。

16、 迭代器模式(Iterator Pattern)
支持访问和遍历对象结构。不但可用于组合结构也可用于集合。介绍提供1种方法顺序访问1个聚合对象中各个元素, 而又不需暴露该对象的内部表示。

17、 中介者模式(Mediator Pattern)
介绍用1个中介对象来封装1系列的对象交互。中介者使各对象不需要显式地相互援用,从而使其耦合疏松,而且可以独立地改变它们之间的交互。

18、 备忘录模式(Memento Pattern)
介绍在不破坏封装性的条件下,捕获1个对象的内部状态,并在该对象以外保存这个状态。这样以后便可将该对象恢复到保存的状态。

19、 视察者模式(Observer Pattern)
介绍定义对象间的1种1对多的依赖关系,以便当1个对象的状态产生改变时,所有依赖于它的对象都得到通知并自动刷新。

20、 状态模式(State Pattern)
介绍允许1个对象在其内部状态改变时改变它的行动。对象看起来仿佛修改了它所属的类。

21、 策略模式(Strategy Pattern)
在对象中封装算法是策略模式的目的。允许不同的格式化算法。

介绍定义1系列的算法,把它们1个个封装起来,并且使它们可相互替换。本模式使得算法的变化可独立于使用它的客户。

模式的主要参与者是Strategy对象(这些对象中封装了不同的算法)和它们的操作环境。Strategy模式利用的关键点在于为Strategy和它的环境设计足够通用的接口,以支持1系列的算法。你没必要为了支持1个新的算法而改变Strategy或它的环境。

22、 模板方法模式(Template Method Pattern)
介绍定义1个操作中的算法的骨架,而将1些步骤延迟到子类中。Template Method使得子类可以不改变1个算法的结构便可重定义该算法的某些特定步骤。

主要解决在软件构建进程中,对某1项任务,常常有稳定的整体操作结构,但各个子步骤却有很多改变的需求,或由于固有的缘由而没法和任务的整体结构同时实现。

23、 访问者模式(Visitor Pattern)
介绍表示1个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的条件下定义作用于这些元素的新操作。

它允许对图元结构所作分析的数目不受限制地增加而没必要改变图元类本身。

访问者类的另外一个优点是它不局限使用于像图元结构这样的组合者,也适用于其他任何对象结构。包括集合、列表,乃至无环有向图。再者,访问者所能访问的类之间无需通过1个公共父类关联起来。也就是说,访问者能逾越类层次结构。

参考:
《设计模式》
http://blog.csdn.net/lovelion

------分隔线----------------------------
------分隔线----------------------------

最新技术推荐