设计模式是面向对象设计中常见问题的典型解决方案。
分为三类:创建型模式:提供了创建对象的机制, 能够提升已有代码的灵活性和可复用性。
结构型模式:介绍如何将对象和类组装成较大的结构, 并同时保持结构的灵活和高效。
行为模式:负责对象间的高效沟通和职责委派。
创建型:
工厂方法:
其在父类中提供一个创建对象的方法, 允许子类决定实例化对象的类型。解决了在不指定具体类的情况下创建产品对象的问题。
实现方式的描述:有一个工厂基类和产品基类,工厂基类中有一个负责对产品类进行实例化的成员函数。在需要增加新的产品时,增加相应的工厂和产品(分别继承自工厂基类和产品基类),这时就可以用一个工厂基类的指针指向具体的产品工厂,从而生成我们需要的产品。
抽象工厂:
是工厂方法的升级版,一个具体工厂支持生成一系列风格相同的产品。比如,生成现代风格的椅子凳子沙发。而另一个具体工厂生成的还是椅子凳子沙发,但是风格是维多利亚风格的。
每种产品(椅子凳子沙发)会有一个基类,还有一个抽象工厂可以生成所有产品。而每一个具体工厂继承自抽象工厂。
生成器:
面对一个复杂庞大的实体,它有很多属性。就像是一栋别墅,它可以有车库,游泳池,雕像,花园。但是这些也不必有。为这种实体编写类时如果将每种情况进行组合,然后在写出很多种类,这无疑很复杂,增加新的属性时,会更复杂。
这时候生成器模式来了,它使你能够分步骤创建复杂对象,允许你使用相同的创建代码生成不同类型和形式的对象。
首先有一个产品基类,里面包括实体的各种属性和方法。抽象建造者类中有产品基类所有属性以及方法。这些方法全都是纯虚函数。另外还有一个具体的建造者,它实现了抽象建造者的所有方法。它也有一个类型是产品类的成员变量,通过那些成员方法可以对产品成员变量增加属性。通过具体建造者可以生成我们想要属性的对象。
另外对于那些经典的产品,我们可以为它们生成一个主管类。里面有各种经典产品的生成步骤,调用相应的生成经典产品的方法,着这些方法里面会执行经典产品的生成步骤,从而生成经典产品。我们的程序中不一定要包括这个主管类,但是主管类对于生成一些经常使用的产品很方便。
原型模式:
抽象产品基类有一个叫clone的成员函数可以返回一个基类实例,具体的产品类会实现clone方法,返回一个具体产品类实例,通过具体产品的clone方法可以返回一个与具体产品一样的实例。我们也可以通过一个中心化的类来保存常用的产品,在使用时直接通过保存的产品来进行克隆。
单例模式:
单例模式保证类只有一个实例,并提供了该实例的全局访问节点。
这面对只需要一个实例的对象,能有效的节省资源,并且结构清晰。
实现方法:通过将类的构造函数私有化,并且在一个共有的静态方法中返回一个指向这个唯一实例的指针。(这个指针是私有的,在静态方法中首先会判断此指针是否为空,是的话就对类进行实例化,将指针指向这个唯一的实例,如果指针不为空的话,直接返回这个指针。)
结构型:
适配器模式:
当我们的客户端代码需要使用一个类型不相符合的库时,我们可以继承库的代码,重写我们需要的方法,并且在这些方法里,将类型转换为客户端代码需要的数据类型。(个人觉得有些复杂,且鸡肋。)
桥接模式:
指实体的属性会在多维度上增加。比如我们有一个shape类,现在shape包括square和circle,同时这些形状有两种颜色,red和blue,这时我们拥有的类有4种,分别是red square, blue square, red circle, blue circle。如果我们再想添加新的颜色或者形状的话,类的数量增加将会是几何级的。这时我们可以将颜色作为形状类的一个成员变量,对于相同的形状可以传入不同的颜色。二维以上的属性亦然。
组合模式:
组合是一种结构型设计模式, 你可以使用它将对象组合成树状结构, 并且能像使用独立对象一样使用它们。比如我们要计算快递包裹类的价值,快递包裹里面可能有单个的物品,也可能有小的包裹,小的包裹的价值是包裹的价值加上内部物品的价值。因为我们也很难知道包裹里面有多少物品和包裹,直接计算其价值的话会很困难。如果我们用包裹和物品类实现同一个名叫component的接口,然后在包裹类中增加component一个数组,并将包裹类以及物品类设置一个指向装它的包裹的对象,这时候,我们可以在包裹中添加物品和包裹,并在每一个包裹或者物品中计算内部物品的价值。
装饰模式:
这个模式就像穿衣服,增加一个功能就穿上一件。实现上:有一个功能接口,有一个具体功能的类实现了这一接口,然后装饰类也实现了这一功能接口,并且有一个成员变量是指向功能接口对象的指针。然后再具体的装饰类中实现相应的功能,并且可以用具体的类装饰类来作为不同功能装饰类的成员变量(之前提到的指针)。
外观模式:
当我们面对一个具有复杂功能的框架,且我们只需要框架的几个类来完成我们的功能。这时候,我们可以使用外观模式,来避免对框架的所有类进行初始化且管理其依赖关系。我们就我们的功能封装在一个外观类中(这个类是一个上帝对象,里面用很多类去完成了我们所需的功能)当需要这个功能的时候直接调用这个外观类即可。
享元模式:
将不同类和(或)对象的公共部分(都有的成员变量,函数)抽离出来,放在一个所有这些对象都能访问的载体中。从而节省内存。
代理模式:
面对现有的类,有时需要对调用类方法前后进行一些操作(缓存,访问限制等),这时候我们可以用代理模式,创建一个代理类,该代理类实现的接口与实体类相同。客户端的请求会到代理类,代理类对其进行一定的处理之后会决定是否将请求返回给实体类。
行为模式:
责任链模式:
将功能或者逻辑结构链接成链表一样的结构。这需要功能或逻辑结构类实现同一个接口。使得整体的结构简单明了,且易于添加新的单元。
命令模式:
它可将请求转换为一个包含与请求相关的所有信息的独立对象。 该转换让你能根据不同的请求将方法参数化、 延迟请求执行或将其放入队列中, 且能实现可撤销操作。
迭代器模式:
我也没想到此模式就是stl容器中迭代器的实现思想。
迭代器模式避免了对容器元素固定的便利方式,而是提供了一种访问元素的方式:迭代器提供了一种类似指针的功能,一般都是指向容器的首元素(对于关联式容器,插入顺序对元素之间的位置关系(不管是空间还是时间)没有影响。),可以通过加减操作来改变迭代器指向元素的位置。(这点可以参考vector的迭代器。)
中介者模式:
如果组件之间耦合度非常大,通过中介者模式可以消除组建之前的耦合度,从而带来各种好处。组件执行某项操作之后会通知中介者,并带上想要进行的操作,之后通过中介者来完成操作。而不是在组件之中完成。
备忘录模式:
这个模式可以生成对象快照,来实现操作回退(状态回滚,抑或是ctrl+z的撤销操作),且能保护原始对象的结构免于暴露。将原始对象设置为备忘录类的一个成员变量,通过backup函数可以将原始对象的状态保存在一个私有成员数组中,同时通过一个undo()操作可以通过将原始对象赋值为之前保存的状态以实现状态回退。
观察者模式:
用于一个实体需要对另一个实体的变化做出反应的情况。例如书店上新书时,需要通知订阅者。这时候书店是subject类,而订阅者就是观察者类。我们会在subject类中定一个数组来存储订阅者的引用,并在购买新书时便利数组,调用观察者类的update()方法。在update方法里面,我们可以书写需要的逻辑。
状态模式:
此设计模式与状态机的概念密切相关。有时候我们的程序需要在特定事件发生时,转换其状态。如果状态随着时间的推进需要不断增加,或者转移的方式不断发生改变的时候。代码的维护就会异常困难。这时候我们需要将状态封装成类,所有的状态都实现相同的接口,在状态转换的时候直接将表示状态的变量重新赋值,这样增添状态或是改变状态的转移方式都会轻松异常,不必修改之前的代码。
策略模式:
让你定义一系列算法,并将每种算法分别放入独立的类中,以使算法的对象能够相互替换。在增加新的算法时不必倍增代码的主体部分。而是使算法实现同一个接口,在算法的调用者类中替换不同的算法对象。
模板方法模式:
模板方法模式建议将算法分解为一系列步骤, 然后将这些步骤改写为方法, 最后在 “模板方法” 中依次调用这些方法。 步骤可以是抽象的, 也可以有一些默认的实现。 为了能够使用算法,客户端需要自行提供子类并实现所有的抽象步骤。 如有必要还需重写一些步骤 (但这一步中不包括模板方法自身)。
访问者模式:
对于已经上线的程序,需要对其对象树进行遍历。如果重新在程序中新增遍历代码,不仅会有很大的安全隐患,你们的系统架构师也不会同意。这时可以用到访问者模式,提供了一个访问者基类,里面包含所有需要遍历的对象。对于不同的遍历方式,用不同的方式实现访问者基类。这时不仅不会在经过测试的代码中重新添加代码,遇到新的遍历方式时,也可以方便扩展。