⼤话设计模式——C++版本
⼀、什么是设计模式
设计模式(Design pattern)是⼀套被反复使⽤、多数⼈知晓的、经过分类编⽬的、代码设计经验的总结。使⽤设计模式是为了可重⽤代码、让代码更容易被他⼈理解、保证代码可靠性。毫⽆疑问,设计模式于⼰于他⼈于系统都是多赢的,设计模式使代码编制真正⼯程化,设计模式是软件⼯程的基⽯,如同⼤厦的⼀块块砖⽯⼀样。项⽬中合理的运⽤设计模式可以完美的解决很多问题,每种模式在现在中都有相应的原理来与之对应,每⼀个模式描述了⼀个在我们周围不断重复发⽣的问题,以及该问题的核⼼解决⽅案,这也是它能被⼴泛应⽤的原因。简单说:
模式:在某些场景下,针对某类问题的某种通⽤的解决⽅案。
场景:项⽬所在的环境
问题:约束条件,项⽬⽬标等
解决⽅案:通⽤、可复⽤的设计,解决约束达到⽬标。
⼆、设计模式的三个分类
创建型模式:对象实例化的模式,创建型模式⽤于解耦对象的实例化过程。
结构型模式:把类或对象结合在⼀起形成⼀个更⼤的结构。
⾏为型模式:类和对象如何交互,及划分责任和算法。
如下图所⽰:
三、各分类中模式的关键点
四、概说23种设计模式
1.单例模式
单例模式,它的定义就是确保某⼀个类只有⼀个实例,并且提供⼀个全局访问点。
单例模式具备典型的3个特点:1、只有⼀个实例。 2、⾃我实例化。 3、提供全局访问点。
因此当系统中只需要⼀个实例对象或者系统中只允许⼀个公共访问点,除了这个公共访问点外,不能通过其他访问点访问该实例时,可以使⽤单例模式。
单例模式的主要优点就是节约系统资源、提⾼了系统效率,同时也能够严格控制客户对它的访问。也许就是因为系统中只有⼀个实例,这样就导致了单例类的职责过重,违背了“单⼀职责原则”,同时也没有抽象类,所以扩展起来有⼀定的困难。其UML结构图⾮常简单,就只有⼀个类,如下图:
2.⼯⼚⽅法模式
作为抽象⼯⼚模式的孪⽣兄弟,⼯⼚⽅法模式定义了⼀个创建对象的接⼝,但由⼦类决定要实例化的类是哪⼀个,也就是说⼯⼚⽅法模式让实例化推迟到⼦类。
⼯⼚⽅法模式⾮常符合“开闭原则”,当需要增加⼀个新的产品时,我们只需要增加⼀个具体的产品类和与之对应的具体⼯⼚即可,⽆须修改原有系统。同时在⼯⼚⽅法模式中⽤户只需要知道⽣产产品的具体⼯⼚即可,⽆须关系产品的创建过程,甚⾄连具体的产品类名称都不需要知道。虽然他很好的符合了“开闭原则”,但是由于每新增⼀个新产品时就需要增加两个类,这样势必会导致系统的复杂度增加。其UML结构图:
3.抽象⼯⼚模式
所谓抽象⼯⼚模式就是提供⼀个接⼝,⽤于创建相关或者依赖对象的家族,⽽不需要明确指定具体类。他允许客户端使⽤抽象的接⼝来创建⼀组相关的产品,⽽不需要关系实际产出的具体产品是什么。这样⼀来,客户就可以从具体的产品中被解耦。它的优点是隔离了具体类的⽣
成,使得客户端不需要知道什么被创建了,⽽缺点就在于新增新的⾏为会⽐较⿇烦,因为当添加⼀个新的产品对象时,需要更加需要更改接⼝及其下所有⼦类。其UML结构图如下:
4.建造者模式
对于建造者模式⽽已,它主要是将⼀个复杂对象的构建与表⽰分离,使得同样的构建过程可以创建不同的表⽰。适⽤于那些产品对象的内部结构⽐较复杂。
建造者模式将复杂产品的构建过程封装分解在不同的⽅法中,使得创建过程⾮常清晰,能够让我们更加精确的控制复杂产品对象的创建过程,同时它隔离了复杂产品对象的创建和使⽤,使得相同的创建过程能够创建不同的产品。但是如果某个产品的内部结构过于复杂,将会导致整个系统变得⾮常庞⼤,不利于控制,同时若⼏个产品之间存在较⼤的差异,则不适⽤建造者模式,毕竟这个世界上存在相同点⼤的两个产品并不是很多,所以它的使⽤范围有限。其UML结构图:
5.原型模式
在我们应⽤程序可能有某些对象的结构⽐较复杂,但是我们⼜需要频繁的使⽤它们,如果这个时候我们来不断的新建这个对象势必会⼤⼤损耗系统内存的,这个时候我们需要使⽤原型模式来对这个结构复杂⼜要频繁使⽤的对象进⾏克隆。所以原型模式就是⽤原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象。
它主要应⽤与那些创建新对象的成本过⼤时。它的主要优点就是简化了新对象的创建过程,提⾼了效率,同时原型模式提供了简化的创建结构。UML结构图:
模式结构
原型模式包含如下⾓⾊:
Prototype:抽象原型类
ConcretePrototype:具体原型类
Client:客户类
6.适配器模式
在我们的应⽤程序中我们可能需要将两个不同接⼝的类来进⾏通信,在不修改这两个的前提下我们可能会需要某个中间件来完成这个衔接的过程。这个中间件就是适配器。所谓适配器模式就是将⼀个类的接⼝,转换成客户期望的另⼀个接⼝。它可以让原本两个不兼容的接⼝能够⽆缝完成对接。
博客为什么没人用了作为中间件的适配器将⽬标类和适配者解耦,增加了类的透明性和可复⽤性。
适配器模式包含如下⾓⾊:
Target:⽬标抽象类
Adapter:适配器类
Adaptee:适配者类
Client:客户类
7.桥接模式
如果说某个系统能够从多个⾓度来进⾏分类,且每⼀种分类都可能会变化,那么我们需要做的就是讲这多个⾓度分离出来,使得他们能独⽴变化,减少他们之间的耦合,这个分离过程就使⽤了桥接模式。所谓桥接模式就是讲抽象部分和实现部分隔离开来,使得他们能够独⽴变化。
桥接模式将继承关系转化成关联关系,封装了变化,完成了解耦,减少了系统中类的数量,也减少了代码量。
桥接模式包含如下⾓⾊:
Abstraction:抽象类
RefinedAbstraction:扩充抽象类
Implementor:实现类接⼝
ConcreteImplementor:具体实现类
8.组合模式
组合模式组合多个对象形成树形结构以表⽰“整体-部分”的结构层次。它定义了如何将容器对象和叶⼦对象进⾏递归组合,使得客户在使⽤的过程中⽆须进⾏区分,可以对他们进⾏⼀致的处理。在使⽤组合模式中需要注意⼀点也是组合模式最关键的地⽅:叶⼦对象和组合对象实现相同的接⼝。这就是组合模式能够将叶⼦节点和对象节点进⾏⼀致处理的原因。
虽然组合模式能够清晰地定义分层次的复杂对象,也使得增加新构件也更容易,但是这样就导致了系统的设计变得更加抽象,如果系统的业务规则⽐较复杂的话,使⽤组合模式就有⼀定的挑战了。
模式结构
组合模式包含如下⾓⾊:
Component: 抽象构件
Leaf: 叶⼦构件
Composite: 容器构件
Client: 客户类
9.装饰模式
我们可以通过继承和组合的⽅式来给⼀个对象添加⾏为,虽然使⽤继承能够很好拥有⽗类的⾏为,但是它存在⼏个缺陷:⼀、对象之间的关系复杂的话,系统变得复杂不利于维护。⼆、容易产⽣“类爆炸”现象。三、是静态的。在这⾥我们可以通过使⽤装饰者模式来解决这个问题。
装饰者模式,动态地将责任附加到对象上。若要扩展功能,装饰者提供了⽐继承更加有弹性的替代⽅案。虽然装饰者模式能够动态将责任附加到对象上,但是他会产⽣许多的细⼩对象,增加了系统的复杂度。
模式结构
装饰模式包含如下⾓⾊:
Component: 抽象构件
ConcreteComponent: 具体构件
Decorator: 抽象装饰类
ConcreteDecorator: 具体装饰类
10.外观模式
我们都知道类与类之间的耦合越低,那么可复⽤性就越好,如果两个类不必彼此通信,那么就不要让这两个类发⽣直接的相互关系,如果需要调⽤⾥⾯的⽅法,可以通过第三者来转发调⽤。外观模式⾮常好的诠释了这段话。外观模式提供了⼀个统⼀的接⼝,⽤来访问⼦系统中的⼀接⼝。它让⼀个应⽤程序中⼦系统间的相互依赖关系减少到了最少,它给⼦系统提供了⼀个简单、单⼀的屏障,客户通过这个屏障来与⼦系统进⾏通信。通过使⽤外观模式,使得客户对⼦系统的引⽤变得简单了,实现了客户与⼦系统之间的松耦合。但是它违背了“开闭原则”,因为增加新的⼦系统可能需要修改外观类或客户端的源代码。
外观模式包含如下⾓⾊:
Facade: 外观⾓⾊
SubSystem:⼦系统⾓⾊
11.享元模式
在⼀个系统中对象会使得内存占⽤过多,特别是那些⼤量重复的对象,这就是对系统资源的极⼤浪费。享元模式对对象的重⽤提供了⼀种解决⽅案,它使⽤共享技术对相同或者相似对象实现重⽤。享元模式就是运⾏共享技术有效地⽀持⼤量细粒度对象的复⽤。系统使⽤少量对象,⽽且这些都⽐较相似,状态变化⼩,可以实现对象的多次复⽤。这⾥有⼀点要注意:享元模式要求能够共享的对象必须是细粒度对象。享元模式通过共享技术使得系统中的对象个数⼤⼤减少了,同时享元模式使⽤了内部状态和外部状态,同时外部状态相对独⽴,不会影响到内部状态,所以享元模式能够使得享元对象在不同的环境下被共享。同时正是分为了内部状态和外部状态,享元模式会使得系统变得更加复杂,同时也会导致读取外部状态所消耗的时间过长。
享元模式包含如下⾓⾊:
Flyweight: 抽象享元类
ConcreteFlyweight: 具体享元类
UnsharedConcreteFlyweight: ⾮共享具体享元类
FlyweightFactory: 享元⼯⼚类
12.代理模式
代理模式就是给⼀个对象提供⼀个代理,并由代理对象控制对原对象的引⽤。它使得客户不能直接与真正的⽬标对象通信。代理对象是⽬标对象的代表,其他需要与这个⽬标对象打交道的操作都是和这个代理对象在交涉。
代理对象可以在客户端和⽬标对象之间起到中介的作⽤,这样起到了的作⽤和保护了⽬标对象的,同时也在⼀定程度上⾯减少了系统的耦合度。
代理模式包含如下⾓⾊:
Subject: 抽象主题⾓⾊
Proxy: 代理主题⾓⾊
RealSubject: 真实主题⾓⾊
13.访问者模式
访问者模式俗称23⼤设计模式中最难的⼀个。除了结构复杂外,理解也⽐较难。在我们软件开发中我们可能会对同⼀个对象有不同的处理,如果我们都做分别的处理,将会产⽣灾难性的错误。对于这种问题,访问者模式提供了⽐较好的解决⽅案。访问者模式即表⽰⼀个作⽤于某对象结构中的各元素的操作,它使我们可以在不改变各元素的类的前提下定义作⽤于这些元素的新操作。
访问者模式的⽬的是封装⼀些施加于某种数据结构元素之上的操作,⼀旦这些操作需要修改的话,接受这个操作的数据结构可以保持不变。为不同类型的元素提供多种访问操作⽅式,且可以在不修改原有系统的情况下增加新的操作⽅式。同时我们还需要明确⼀点那就是访问者模式是适⽤于那些数据结构⽐较稳定的,因为他是将数据的操作与数据结构进⾏分离了,如果某个系统的数据结构相对稳定,但是操作算法易于变化的话,就⽐较适⽤适⽤访问者模式,因为访问者模式使得算法操作的增加变得⽐较简单了。
访问者模式包含如下⾓⾊:
Vistor: 抽象访问者
ConcreteVisitor: 具体访问者
Element: 抽象元素
ConcreteElement: 具体元素
ObjectStructure: 对象结构
14.模板模式
有些时候我们做某⼏件事情的步骤都差不多,仅有那么⼀⼩点的不同,在软件开发的世界⾥同样如此,如果我们都将这些步骤都⼀⼀做的话,费时费⼒不讨好。所以我们可以将这些步骤分解、封装起来,然后利⽤继承的⽅式来继承即可,当然不同的可以⾃⼰重写实现嘛!这就是模板⽅法模式提供的解决⽅案。
所谓模板⽅法模式就是在⼀个⽅法中定义⼀个算法的⾻架,⽽将⼀些步骤延迟到⼦类中。模板⽅法使得⼦类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。
模板⽅法模式就是基于继承的代码复⽤技术的。在模板⽅法模式中,我们可以将相同部分的代码放在⽗类中,⽽将不同的代码放⼊不同的⼦类中。也就是说我们需要声明⼀个抽象的⽗类,将部分逻辑以具体⽅法以及具体构造函数的形式实现,然后声明⼀些抽象⽅法让⼦类来实现剩余的逻辑,不同的⼦类可以以不同的⽅式来实现这些逻辑。所以模板⽅法的模板其实就是⼀个普通的⽅法,只不过这个⽅法是将算法实现的步骤封装起来的。
模板⽅法模式包含如下⾓⾊:
AbstractClass: 抽象类
ConcreteClass: 具体⼦类
15.策略模式
我们知道⼀件事可能会有很多种⽅式来实现它,但是其中总有⼀种最⾼效的⽅式,在软件开发的世界⾥⾯同样如此,我们也有很多中⽅法来实现⼀个功能,但是我们需要⼀种简单、⾼效的⽅式来实现它,使得系统能够⾮常灵活,这就是策略模式。
所以策略模式就是定义了算法族,分别封装起来,让他们之前可以互相转换,此模式然该算法的变化独⽴于使⽤算法的客户。
在策略模式中它将这些解决问题的⽅法定义成⼀个算法,每⼀个⽅法都对应着⼀个具体的算法,这⾥的⼀个算法我就称之为⼀个策略。虽然策略模式定义了算法,但是它并不提供算法的选择,即什么算法对于什么问题最合适这是策略模式所不关⼼的,所以对于策略的选择还是要客户端来做。客户必须要清楚的知道每个算法之间的区别和在什么时候什么地⽅使⽤什么策略是最合适的,这样就增加客户端的负担。
同时策略模式也⾮常完美的符合了“开闭原则”,⽤户可以在不修改原有系统的基础上选择算法或⾏为,也可以灵活地增加新的算法或⾏为。但是⼀个策略对应⼀个类将会是系统产⽣很多的策略类。
策略模式包含如下⾓⾊:
Context: 环境类
Strategy: 抽象策略类
ConcreteStrategy: 具体策略类
16.状态模式
在很多情况下我们对象的⾏为依赖于它的⼀个或者多个变化的属性,这些可变的属性我们称之为状态,也就是说⾏为依赖状态,即当该对象因为在外部的互动⽽导致他的状态发⽣变化,从⽽它的⾏为也会做出相应的变化。对于这种情况,我们是不能⽤⾏为来控制状态的变化,⽽应该站在状态的⾓度来思考⾏为,即是什么状态就要做出什么样的⾏为。这个就是状态模式。
所以状态模式就是允许对象在内部状态发⽣改变时改变它的⾏为,对象看起来好像修改了它的类。
在状态模式中我们可以减少⼤块的if…else语句,它是允许态转换逻辑与状态对象合成⼀体,但是减少if…else语句的代价就是会换来⼤量的类,所以状态模式势必会增加系统中类或者对象的个数。
同时状态模式是将所有与某个状态有关的⾏为放到⼀个类中,并且可以⽅便地增加新的状态,只需要改变对象状态即可改变对象的⾏为。但是这样就会导致系统的结构和实现都会⽐较复杂,如果使⽤不当就会导致程序的结构和代码混乱,不利于维护。
状态模式包含如下⾓⾊:
Context: 环境类
State: 抽象状态类
ConcreteState: 具体状态类
17.观察者模式
何谓观察者模式?观察者模式定义了对象之间的⼀对多依赖关系,这样⼀来,当⼀个对象改变状态时,它的所有依赖者都会收到通知并且⾃动更新。
在这⾥,发⽣改变的对象称之为观察⽬标,⽽被通知的对象称之为观察者。⼀个观察⽬标可以对应多个观察者,⽽且这些观察者之间没有相互联系,所以么可以根据需要增加和删除观察者,使得系统更易于扩展。所以观察者提供了⼀种对象设计,让主题和观察者之间以松耦合的⽅式结合。
观察者模式包含如下⾓⾊:
Subject: ⽬标
ConcreteSubject: 具体⽬标
Observer: 观察者
ConcreteObserver: 具体观察者
18.备忘录模式
后悔药⼈⼈都想要,但是事实却是残酷的,根本就没有后悔药可买,但是也不仅如此,在软件的世界⾥就有后悔药!备忘录模式就是⼀种后悔药,它给我们的软件提供后悔药的机制,通过它可以使系统恢复到某⼀特定的历史状态。
所谓备忘录模式就是在不破坏封装的前提下,捕获⼀个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对象恢复到原先保存的状态。它实现了对信息的封装,使得客户不需要关⼼状态保存的细节。保存就要消耗资源,所以备忘录模式的缺点就在于消耗资源。如果类的成员变量过多,势必会占⽤⽐较⼤的资源,⽽且每⼀次保存都会消耗⼀定的内存。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论