http://design-patterns.readthedocs.org/zh_CN/latest/index.html
https://github.com/Waleon/DesignPatterns
代码可以参考这里
设计模式
创建型模式
- 简单工厂模式
- 工厂方法模式
- 抽象工厂模式
- 建造者模式
- 单例模式
简单工厂模式(Simple Factory Pattern)
- 模式动机:将参数传给工厂,工厂根据参数new并return相应的类,由于调用方不需要知道具体的create细节,所以可以降低系统耦合度。
- 代码:
#include "Factory.h"
#include "ConcreteProductA.h"
#include "ConcreteProductB.h"
Product* Factory::createProduct(string proname){
if ( "A" == proname )
{
return new ConcreteProductA();
}
else if("B" == proname)
{
return new ConcreteProductB();
}
return NULL;
}
如果参数类型较多,也可以用switch分case。
- 缺点
- Factory类可能随着参数增多而变得过重;
- Factory类如果出现问题,会影响所有产品的生产。
- 优点
- 调用者不需要直接create产品,而是把责任交给Factory。分离实例的创建与使用,降低了系统耦合度;
- 调用者不需要知道产品的类名,而只需要记住产品对应的参数;
- 通过引入配置文件,可以在不修改任何客户端代码的情况下更换和增加新的具体产品类,在一定程度上提高了系统的灵活性。
工厂方法模式(Factory Method Pattern)
- 模式动机:为了避免Factory类过重,将Factory类定义为抽象类。实现上,继承于AFactory、BFactory类来生产A、B类产品。
- 可以看到Factory和Product都是抽象类,实现则延迟到不同的产品类、工厂类中
- 代码:
#include "concrete_factory.h"
#include "product.h"
#include <iostream>
#ifndef SAFE_DELETE
#define SAFE_DELETE(p) { if(p){delete(p); (p)=NULL;} }
#endif
int main()
{
// 奔驰
AFactory *pFactory = new BenzFactory();
ICar *pCar = pFactory->CreateCar();
cout << "Benz factory: " << pCar->Name() << endl;
SAFE_DELETE(pCar);
SAFE_DELETE(pFactory);
// 宝马
pFactory = new BmwFactory();
pCar = pFactory->CreateCar();
cout << "Bmw factory: " << pCar->Name() << endl;
SAFE_DELETE(pCar);
SAFE_DELETE(pFactory);
// 奥迪
pFactory = new AudiFactory();
pCar = pFactory->CreateCar();
cout << "Audi factory: " << pCar->Name() << endl;
SAFE_DELETE(pCar);
SAFE_DELETE(pFactory);
getchar();
return 0;
}
-
从上述代码可以看出,工厂方法模式比起简单工厂模式多了对Factory类的抽象化及继承实现,在Factory类中只定义了具体工厂子类必须实现的接口,从而减轻了Factory类的工作复杂度及维护难度。
-
优缺点:在引入新产品时,只需要添加一个工厂子类及产品子类,但编译复杂度也成对提高。
-
适用场景
- 调用者不知道产品类名,但知道工厂类名;
- 将创建对象的任务委托给多个工厂子类中的某一个,客户端在使用时可以无须关心是哪一个工厂子类创建产品子类,需要时再动态指定,可将具体工厂类的类名存储在配置文件或数据库中。
抽象工厂模式(Abstract Factory)
- 模式动机:在工厂方法模式中,产品(比如A电饭煲,B电饭煲,等等电饭煲)只有use()这样的api。那假如A、B这些工厂不仅生产电饭煲,还生产水果,需要eat()这样的api呢?A水果与A电饭煲同属于A的产品族,这时产品类会有水果抽象类、电饭煲抽象类及其子类实现。而工厂类会有A类、B类及其子类实现。
- 上图中,1、2是两个不同工厂,均实现了createProductA和createProductB,而A、B产品均继承于抽象类。
- 优点
- 高内聚低耦合
- 一个工厂总是生产同一产品族内的产品
- 增加一对新工厂和产品族容易
- 缺点
- 添加新产品时,不仅需要扩展新产品api,还需要修改抽象工厂接口
- 适用场景
- 一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节,这对于所有类型的工厂模式都是重要的。
- 系统中有多于一个的产品族,而每次只使用其中某一产品族。
- 属于同一个产品族的产品将在一起使用,这一约束必须在系统的设计中体现出来。
- 系统提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于具体实现。
- 实例
- 比如软件经常提供多套界面主题,要求界面中的按钮、文本框、背景色等一起发生改变时,这里可以以主题为抽象工厂。
建造者模式
- 模式动机:隐藏对象的具体build细节。
- 模式角色
- Director(指挥者)
- Builder(抽象构建器)
- ConcreteBuilder(具体构建器)
- Product(产品)
#include "concrete_bulider.h"
#include "director.h"
#include <string>
#ifndef SAFE_DELETE
#define SAFE_DELETE(p) { if(p){delete(p); (p)=NULL;} }
#endif
int main()
{
Direcror *pDirecror = new Direcror();
ThinkPadBuilder *pTPBuilder = new ThinkPadBuilder();
YogaBuilder *pYogaBuilder = new YogaBuilder();
// 组装 ThinkPad、Yoga
pDirecror->Create(pTPBuilder);
pDirecror->Create(pYogaBuilder);
// 获取组装后的电脑
Computer *pThinkPadComputer = pTPBuilder->GetResult();
Computer *pYogaComputer = pYogaBuilder->GetResult();
// 测试输出
cout << "-----ThinkPad-----" << endl;
cout << "CPU: " << pThinkPadComputer->GetCPU() << endl;
cout << "Mainboard: " << pThinkPadComputer->GetMainboard() << endl;
cout << "Ram: " << pThinkPadComputer->GetRam() << endl;
cout << "VideoCard: " << pThinkPadComputer->GetVideoCard() << endl;
cout << "-----Yoga-----" << endl;
cout << "CPU: " << pYogaComputer->GetCPU() << endl;
cout << "Mainboard: " << pYogaComputer->GetMainboard() << endl;
cout << "Ram: " << pYogaComputer->GetRam() << endl;
cout << "VideoCard: " << pYogaComputer->GetVideoCard() << endl;
SAFE_DELETE(pThinkPadComputer);
SAFE_DELETE(pYogaComputer);
SAFE_DELETE(pTPBuilder);
SAFE_DELETE(pYogaBuilder);
SAFE_DELETE(pDirecror);
getchar();
return 0;
}
- 上述代码中有
new ThinkPadBuilder()
和new YogaBuilder()
,如果需要更多种builder,这里是可以用enum、switch、if等原语优化可读性的。 - 由上述代码可看出,Product的组装及获取均发生在Builder内部,我们使用建造者时不需要关心这些构建与返回细节。Director用于指挥组装,代码如下:
//director.h
#ifndef DIRECTOR_H
#define DIRECTOR_H
#include "builder.h"
// 构造指挥官
class Direcror
{
public:
void Create(IBuilder *builder) {
builder->BuildCpu();
builder->BuildMainboard();
builder->BuildRam();
builder->BuildVideoCard();
}
};
#endif // DIRECTOR_H
单例模式
- 模式动机:希望某一个类只有一个实例。将构造函数私有化,并用一个静态工厂生产实例产品,该工厂方法负责检验实例的存在性并实例化自己,然后存储在静态成员变量中,以确保只有一个实例被创建。
- cpp11实现
- 优点
- 只有一个实例,节省创建、销毁的开销
- 单例模式可以扩展为多例模式,同样能达到控制实例数目的效果
- 缺点
- 单例类的职责过重,在一定程度上违背了“单一职责原则”。因为单例类既充当了工厂角色,提供了工厂方法,同时又充当了产品角色,包含一些业务方法,将产品的创建和产品的本身的功能融合到一起。
- 滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;现在很多面向对象语言(如Java、C#)的运行环境都提供了自动垃圾回收的技术,因此,如果实例化的对象长时间不被利用,系统会认为它是垃圾,会自动销毁并回收资源,下次利用时又将重新实例化,这将导致对象状态的丢失。
结构型模式
适配器模式(包装器)
- 模式动机:适配器(Adapter)提供客户类需要的接口,适配器的实现就是把客户类的请求转化为对适配者的相应接口的调用。也就是说:当客户类调用适配器的方法时,在适配器类的内部将调用适配者(Adaptee)类的方法,而这个过程对客户类是透明的,客户类并不直接访问适配者类。因此,适配器可以使由于接口不兼容而不能交互的类可以一起工作。这就是适配器模式的模式动机。
行为型模式
观察者模式
- 模式动机:建立一种对象与对象之间的依赖关系,一个对象发生改变时将自动通知其他对象,其他对象将相应做出反应。在此,发生改变的对象称为观察目标,而被通知的对象称为观察者,一个观察目标可以对应多个观察者,而且这些观察者之间没有相互联系,可以根据需要增加和删除观察者,使得系统更易于扩展。
- 模式成员
- Subject: 目标
- ConcreteSubject: 具体目标
- Observer: 观察者
- ConcreteObserver: 具体观察者
- UML图
- 时序图
- 代码实现
- 在Subject对象里存放着Observer指针的list,用push_back实现attach,remove实现detach;
- 在通知所有观察者时,遍历以上list,进行对应update操作。
- 优点
- 定义了稳定的消息更新传递机制;
- 支持广播通信;
- 符合"开闭原则"。
- 缺点
- 如果一个观察目标对象有很多直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间;
- 如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃;
- 观察者只知道观察目标对象发生了变化,但不知道发生了怎样的变化(这与update传递的参数有关)。
- MVC模式
MVC模式是一种架构模式,它包含三个角色:模型(Model),视图(View)和控制器(Controller)。观察者模式可以用来实现MVC模式,观察者模式中的观察目标就是MVC模式中的模型(Model),而观察者就是MVC中的视图(View),控制器(Controller)充当两者之间的中介者(Mediator)。当模型层的数据发生改变时,视图层将自动改变其显示内容。