设计模式
设计模式分类
1)根据目的分为三类: 创建型,结构型和行为型;
- 创建型模式主要用于创建对象。
- 结构型模式主要用于处理类或对象的组合。
- 行为型模式主要用于描述对类或对象怎样交互和怎样分配职责。
2)根据范围:类模式和对象模式
-
类模式: 处理类和子类之间的关系,这些关系通过继承建立,在编译时刻就被 确定下来,是属于静态的。
-
对象模式:处理对象间的关系,这些关系在运行时刻变化,更具动态性。
3)设计模式的六大原则
-
“开-闭”原则(OCP):对扩展开放,对修改关闭。
-
在一个系统中,对于扩展是开放的,对于修改是关闭的,一个好的系统是在不修 改源代码的情况下,可以扩展你的功能…而实现开闭原则的关键就是抽象化.
-
通过扩展已有软件系统,可以提供新的行为,以满足对软件的新的需求,使变化中 的软件有一定的适应性和灵活性。已有软件模块,特别是最重要的抽象层模块不能 再修改,这使变化中的软件系统有一定的稳定性和延续性。
在"开-闭"原则中,不允许修改的是抽象的类或者接口,允许扩展的是具体的实现类,抽 象类和接口在"开-闭"原则中扮演着极其重要的角色…即要预知可能变化的需求.又预 见所有可能已知的扩展…所以在这里"抽象化"是关键!!!
可变性的封闭原则:找到系统的可变因素,将它封装起来…这是对"开-闭"原则最好的实 现…不要把你的可变因素放在多个类中,或者散落在程序的各个角落…你应该将可变的 因素,封套起来…并且切忌不要把所用的可变因素封套在一起…最好的解决办法是,分 块封套你的可变因素!!避免超大类,超长类,超长方法的出现!!给你的程序增加艺术气 息,将程序艺术化是我们的目标!!
-
-
里氏代换原则:任何基类可以出现的地方,子类也可以出现
- 子类能够必须能够替换基类能够从 出现的地方。子类也能在基类 的基础上新增行为。这里讲的是基类和子类的关系, 只有这种关系存在时,里氏代换原则才存在。正方形是长方形是理解里氏代换原则 的经典例子。
-
依赖倒转原则:要依赖抽象,而不要依赖具体的实现
- (1)抽象不应当依赖于细节;细节应当依赖于抽象; (2)要针对接口编程,不针 对实现编程。
-
合成/聚合复用原则(CARP):要尽量使用合成/聚合原则,而不是继承关系达到软件复用的目的。
- 合成/聚合复用原则(Composite/Aggregate ReusePrinciple或CARP)经常又叫做合成 复用原则(Composite ReusePrinciple或CRP),就是在一个新的对象里面使用一些 已有的对象,使之成为新对象的一部分;新对象通过向这些对象的委派达到复用已 有功能的目的。简而言之,要尽量使用合成/聚合,尽量不要使用继承。
-
最少知识原则:系统中的类,尽量不要与其他类互相作用,减少类之间的耦合度
-
一个对象应当对其它对象有尽可能少的了 解。
其它表述:只与你直接的朋友们通信,不要跟"陌生人"说话。一个类应该对自己需 要耦合或调用的类知道得最少,你(被耦合或调用的类)的内部是如何复杂都和我 没关系,那是你的事情,我就知道你提供的public方法,我就调用这么多,其他的 一概不关心。
-
-
接口隔离原则:使用多个专门的接口比使用单一的总接口总要好。法则与迪米特法则(最少知识原则)是相通的。
- 避免一个接口过于臃肿,使得一个依赖于该接口的类,还要操心这个接口里面,它自身不需要的那些函数。
设计模式简介
- Abstract Factory 抽象工厂模式:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
- Adapter适配器模式:将一个类的接口转换成客户希望的另外一个接口。 Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工 作。
- Bridge桥接模式:将抽象部分与它的实现部分分离,使它们都可以独立地变 化。
- Builder建造者模式:将一个复杂对象的构建与它的表示分离,使得同样的构建 过程可以创建不同的表示。
- Chain of Responsibility 职责链:为解除请求的发送者和接收者之间耦合,而使 多个对象都有机会处理这个请求。将这些对象连成一条链,并沿着这条链传递 该请求,直到有一个对象处理它。
- Command命令模式:将一个请求封装为一个对象,从而使你可用不同的请求对 客户进行参数化;对请求排队或记录请求日志,以及支持可取消的操作。
- Composite 组合模式:【二叉树的根和叶子】将对象组合成树形结构以表示“部分-整体”的层次结构。 它使得客户对单个对象和复合对象的使用具有一致性。
- Decorator 装饰器:动态地给一个对象添加一些额外的职责。就扩展功能而言, 它比生成子类方式更为灵活。
- Facade外观模式:为子系统中的一组接口提供一个一致的界面,Facade模式定 义了一个高层接口,这个接口使得这一子系统更加容易使用。
- Factory Method 工厂方法:定义一个用于创建对象的接口,让子类决定将哪一 个类实例化。Factory Method使一个类的实例化延迟到其子类。
- Flyweight享元模式:运用共享技术有效地支持大量细粒度的对象。
- Interpreter模式:给定一个语言, 定义它的文法的一种表示,并定义一个解释器, 该解释器使用该表示来解释语言中的句子。
- Iterator 迭代器:提供一种方法顺序访问一个聚合对象中各个元素, 而又不需暴 露该对象的内部表示。
- Mediator 中介者:用一个中介对象来封装一系列的对象交互。中介者使各对象 不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的 交互。
- Memento备忘录模式:在不破坏封装性的前提下,捕获一个对象的内部状态, 并在该对象之外保存这个状态。这样以后就可将该对象恢复到保存的状态。
- Observer观察者模式:定义对象间的一种一对多的依赖关系,以便当一个对象的 状态发生改变时,所有依赖于它的对象都得到通知并自动刷新。
- Prototype原型模式:【多类的克隆】用原型实例指定创建对象的种类,并且通过拷贝这个原型 来创建新的对象。
- Proxy 代理模式:为其他对象提供一个代理以控制对这个对象的访问。
- Singleton单例模式:【一个世界】保证一个类仅有一个实例,并提供一个访问它的全局访问 点。 State 状态:允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎 修改了它所属的类。
- Strategy 策略模式:定义一系列的算法,把它们一个个封装起来, 并且使它们可相 互替换。本模式使得算法的变化可独立于使用它的客户。
- Template Method 模板方法:【炒菜步骤】定义一个操作中的算法的骨架,而将一些步骤延迟 到子类中。Template Method使得子类可以不改变一个算法的结构即可重定义该 算法的某些特定步骤。
- Visitor 访问者模式:表示一个作用于某对象结构中的各元素的操作。它使你可 以在不改变各元素的类的前提下定义作用于这些元素的新操作。
单例模式【Singleton Pattern】
-
一个世界只有一个对象
-
实现方式:
1. private 构造函数,析构函数,拷贝构造函数和赋值构造函数 2. 类的内部 定义一个static 的该类的指针 instance 3. 类的内部定义一个public 的获得上述指针的函数,如果该指针为NULL,创建一个返回;否则直接返回 4. 全局初始化instance = NULL
原型模式【prototype pattern】
-
本质上是多态类的克隆clone
-
意义:
Prototype 的意义在于,你拿到一个 Base* ,它指向某个 Derived 对象,你想克隆出 Derived 对象,但代码中不写出 Derived 的具体类型,因为有很多派生类,这种情况下你用构造函数是搞不定的,type-switch 是 bad smells 。
-
意图:
用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
-
适用性:
当一个系统应该独立于他的产品创建、构成和表示时,需要使用原型模式
当要实例化的类是在运行时刻指定时,如通过动态装载
为了避免创建一个与产品类层次平行的工厂类层次时
当一个类的实例只能有几个不同状态组合中的一种时,建立相应数目的原型并克隆他们可能比每次用合适的状态手工实例化该类更方便一些。
-
实现方式
1. 基类 的析构为虚函数,定义一个public的功能是复制的clone函数,且为纯虚函数 2. 派生类A,B 继承自 基类,然后分别实现自己的纯虚函数,只不过clone返回的是派生类类型A,B。
组合模式【Composite Pattern】
- 树形结构,类似于二叉树里面的叶子节点和非叶子节点;无论是不是叶子,操作都一样
- PC用到的文件系统,其实就是我们数据结构里的树形结构,我们处理树中的每个节点时,其实不用考虑他是叶子节点还是根节点,因为他们的成员函数都是一样的,这个就是组合模式的精髓。
- 适用场景:
- 希望用户忽略组合对象与单个对象的不同,统一地使用组合结构中的所有对象。
- 表示对象的“整体-部分”层次结构(树形结构)
- 公司部门管理什么的用组合模式比较好。
- 类别:根据所实现接口的区别分为两种形式,分别称为安全模式和透明模式。组合模式可以不提供父对象的管理方法,但组合模式必须在合适的地方提供子对象的管理方法(诸如:add、remove、getChild等)。
- 透明模式:
- 无论是叶子类还是非叶子类(一般称为Composite类),都有管理子对象的方法(add,remove, getChild等),使得叶子类和非叶子类具备了一致性的特点。
- 缺点: 不够安全,因为树叶类对象和合成类对象在本质上是有区别的。因为叶子类没有儿子了,所以他的add,remove和getchild理应无法操作,比如公司底层员工是没有下属的,所以这种权限应该对他们关闭。但是这种实现允许你这么做,编译时不会出错,执行时才会报错。
- 安全模式:
- 在非叶子类(Composite类)中声明用来管理子对象的方法,而叶子类中不存在这些方法。
- 缺点:不透明,因为叶子类和非叶子类具备不同的接口。
- 透明模式:
- 实现方法
- 安全模式:透明模式只需要在叶子类里面实现那些方法就可以了
1. 涉及到三个角色,基础构件类(基类,声明方法),叶子类(派生类,不实现方法),非叶子类(conposite类,派生类,实现管理子类的方法);
2. 基础构件类,三个方法: add, remove, getChildren;定义一个打印的函数用来显示
3. 叶子类:继承即可,不实现三个方法;实现自己的打印函数
4. 非叶子类: 继承,实现三个方法,实现自己的打印函数注意:基类的构造函数需要设置为protected,毕竟是为了安全性着想
参考链接: 我所理解的设计模式(C++实现)
模板模式
-
流程上的统一,细节上的分离。
-
基于继承的代码复用基本技术。
-
定义: 定义一个模板结构,将具体内容延迟到子类去实现。在不改变模板结构的前提下在子类中重新定义模板中的内容。
-
解决的问题
- 提高代码复用性
将相同部分的代码放在抽象的父类中,而将不同的代码放入不同的子类中 - 实现了反向控制
通过一个父类调用其子类的操作,通过对子类的具体实现扩展不同的行为,实现了反向控制 & 符合“开闭原则”
- 提高代码复用性
-
实现方法
一个简单的例子:炒鸡蛋和炒蒜蓉都需要步骤 A,B,C,D,但是其中B,C两步两者是不一样。
1. 实现一个基类,定义步骤A,B,C,D都是虚函数,定义一个处理步骤,把步骤A,B,C,D一一处理。
2. 实现派生类,复写步骤A,B,C,D即可。
简单工厂模式
-
简单工厂模式定义了创建对象的接口,将创建对象的细节隐藏,产生具体对象时只 需要向工厂提供产品类型,而无需根据每一个具体类依次调用各自的构造函数。 一个工厂生产多种产品,采购商只需要提供所需的产品名称,而无需了解产品是哪 个车间生产的。 简单工厂模式属于类的创建型模式,又叫做静态工厂方法模式。通过专门定义一个类 来负责创建其他类的实例,被创建的实例通常都具有共同的父类。 核心思想是用一个工厂来根据输入的条件产生不同的类,然后根据不同类的virtual 函数得到不同的结果。
-
实现方法:
1.定义一个 零件类和工厂类。零件类是指需要的产品的类,工厂类是指创建零件的类。
-
简单工厂模式并不属于 23 种 GoF 设计模式之一。它是工厂模式家族中最简单实用的模式,可以理解为是不同工厂模式的一个特殊实现。
工厂方法模式
-
简单工厂模式为用户隐藏了生产产品的细节,但若想生产新产品需要修改工厂类, 违背了类设计原则中的开放-封闭原则,即在模块在扩展性方面应该是开放的,在更 改性方面应该是封闭的,工厂方法模式通过添加工厂子类的方式改进了简单工厂模 式,遵循了开放-封闭原则。
-
在简单工厂模式的基础上,将工厂类抽象出来,不再通过一个工厂负责所有产的创建,而是将具体的创建工作交给子类去创建,核心工厂类变成一个抽象的角色,仅负责给出具体工厂类必须实现的接口。
-
实现方法
角色: 抽象产品角色+具体产品角色(没有产品等级结构) 抽象工厂角色+具体工厂角色
抽象工厂模式
-
抽象工厂模式的动机:在工厂方法模式中具体工厂负责生产具体的产品,每一个具体工厂对应一种具体产品,工厂方法也具有唯一性,一般情况下,一个具体工厂中只有一个工厂方法或者一组重载的工厂方法。但是有时候我们需要一个工厂可以提供多个产品对象,而不是单一的产品对象。
- 产品等级结构 :产品等级结构即产品的继承结构,如一个抽象类是电视机,其子类有海尔电视机、海信电视机、TCL电视机,则抽象电视机与具体品牌的电视机之间构成了一个产品等级结构,抽象电视机是父类,而具体品牌的电视机是其子类。
- 产品族 :在抽象工厂模式中,产品族是指由同一个工厂生产的,位于不同产品等级结构中的一组产品,如海尔电器工厂生产的海尔电视机、海尔电冰箱,海尔电视机位于电视机产品等级结构中,海尔电冰箱位于电冰箱产品等级结构中。
- 当系统所提供的工厂所需生产的具体产品并不是一个简单的对象,而是多个位于不同产品等级结构中属于不同类型的具体产品时需要使用抽象工厂模式。
- 抽象工厂模式是所有形式的工厂模式中最为抽象和最具一般性的一种形态。
- 抽象工厂模式与工厂方法模式最大的区别在于,工厂方法模式针对的是一个产品等级结构,而抽象工厂模式则需要面对多个产品等级结构,一个工厂等级结构可以负责多个不同产品等级结构中的产品对象的创建 。当一个工厂等级结构可以创建出分属于不同产品等级结构的一个产品族中的所有对象时,抽象工厂模式比工厂方法模式更为简单、有效率。
-
抽象工厂模式(Abstract Factory Pattern):提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。抽象工厂模式又称为Kit模式,属于对象创建型模式。
-
实现方法:
包含如下角色: 抽象产品+具体产品(产品有产品等级结构和产品族) 抽象工厂+具体工厂
建造者模式
-
建造者模式(Builder Pattern):将产品的内部表象和产品的生成过程分割开来,从而使一个建造过程生 成具有不同的内部表象的产品对象。
-
建造者模式是一步一步创建一个复杂的对象,它允许用户只通过指定复杂对象的类型和内容就可以构建它们,用户不需要知道内部的具体构建细节。建造者模式属于对象创建型模式。根据中文翻译的不同,建造者模式又可以称为生成器模式。
建造模式可以强制实行一种分步骤进行的建造过程。
-
其实是 抽象工厂模式中的 具体工厂A,B,C,D(每个生产一簇产品1,2,3,4)[这就是builder和concrete builder, 类似于抽象工厂中的abstract product和concrete product],然后通过Director 将其每个工厂产生的不同产品组装起来,成为一个product。
-
与工厂模式的对比:
-
Factory模式中
有一个抽象的工厂。 实现一个具体的工厂—汽车工厂。 工厂生产汽车A,得到汽车产品A。 工厂生产汽车B,得到汽车产品B。
这样做,实现了购买者和生产线的隔离。强调的是结果。
Builder模式
引擎工厂生产引擎产品,得到汽车部件A。 轮胎工厂生产轮子产品,得到汽车部件B。 底盘工厂生产车身产品,得到汽车部件C。 将这些部件放到一起,形成刚好能够组装成一辆汽车的整体。 将这个整体送到汽车组装工厂,得到一个汽车产品。
这样做,目的是为了实现复杂对象生产线和其部件的解耦。强调的是过程
两者的区别在于
Factory模式不考虑对象的组装过程,而直接生成一个我想要的对象。
Builder模式先一个个的创建对象的每一个部件,再统一组装成一个对象。
Factory模式所解决的问题是,工厂生产产品。
而Builder模式所解决的问题是工厂控制产品生成器组装各个部件的过程,然后 从产品生成器中得到产品。 Builder模式不是很常用。
-
-
实现方法
Builder: 创建产品的每一个零件,抽象接口
ConcreteBuilder: 具体的创建产品的各个零件,零件A,零件B,零件C
Director: 使用Builder接口创建出各个零件
Product: 表示被构造的复杂对象
适配器模式
-
适配器模式(Adapter Pattern) :将一个接口转换成客户希望的另一个接口,适配器模式使接口不兼容的那些类可以一起工作,其别名为包装器(Wrapper)。适配器模式既可以作为类结构型模式,也可以作为对象结构型模式。
-
实质上参照C++里面的deque变成stack和queue的方式就是典型的适配器模式。
-
实现方法:
Target 目标类
Adapter 适配器类
Adaptee 适配者类
命令模式
- 命令模式(Command Pattern):将一个请求封装为一个对象,从而使我们可用不同的请求对客户进行参数化;对请求排队或者记录请求日志,以及支持可撤销的操作。命令模式是一种对象行为型模式,其别名为动作(Action)模式或事务(Transaction)模式。
- 实现方法:
四个角色:
调用者和接受者是对用户可见的;
接受者有一堆函数,是执行各个命令的函数。
调用者有一堆命令的集合,调用者使用某一个命令就直接执行了对应的操作。
具体命令类里面其实藏着一个接受者,一个具体命令类其实只是执行接受者的一个操作函数。Innover 调用者
Receiver 接受者
Commond 命令抽象类
ConcreteCommand 具体命令类