0. Introduction
接触设计模式有两年时间了,但一直没有系统整理过,为了不至于让自己的思维被繁琐的工作一点点禁锢,还是决定总结一下,为了能够真正做到有所收获,整个系列会按照GoF的Design Patterns: Elements of Reusable Object-Oriented Software的行文思路,但不会照本宣科就是了,Wikipedia上关于23种设计模式的介绍非常全面,CSDN上也可以下载中/英文电子档,因此很多套话、类图一概省去。
最早接触设计模式的时候,难免被各种模式的联系和区别所困扰,从教科书的分析可以得到模式之间形式上的不同。但这样对于领会设计模式意义不大,因为我们掌握模式的目的是为了融会贯通,灵活运用,以对开发有所帮助。
稍微成规模的OO程序,会有大量对象,其中很多实体对象之间存在着父子、兄弟关系,对象的创建提升为一种模式。其好处在于设计模式本身所宣称的reusable,这就像堆积木盖房子一样,堆的好的情况下,换一换门窗便是另一番风景。
关于实现,我不会为了厘清模式间的区别而刻意使用相似代码实现,相反,我会根据模式本身的适用情况举例,而且大量代码基于SourceMaking。
_______________________________
1. Creational Design Patterns(DP)
创建型DP抽象了类和对象的创建过程,GoF给出了5种创建型DP:Abstract Factory、Builder、Factory Method、Builder、Prototype、Singleton。
2. Abstract Factory
意图:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
1) 只提供了一个创建接口,其返回值为具体产品:如AbstractProduct *Client::CreateProduct(AbstractFactory &factory);
2) 接口的参数是一个工厂对象(AbstractFactory &factory
)的引用,参数类型(AbstractFactory
)为抽象基类,调用时根据需要传入具体工厂对象即可;
3) 接口内部实现了一系列相关或相互依赖对象(抽象产品)的创建:当传入具体工厂时,接口实现的就是一系列具体产品的创建;
4) 创建的产品立即返回(CreateProduct
)。
参与者:
• AbstractFactory
— 声明一个创建抽象产品对象的操作接口。
• ConcreteFactory
— 实现创建具体产品对象的操作。
• AbstractProduct
— 为一类产品对象声明一个接口。
• ConcreteProduct
— 定义一个将被相应的具体工厂创建的产品对象。
— 实现AbstractProduct接口。
• Client
— 仅使用由AbstractFactory和AbstractProduct类声明的接口。
代码:
class AbstractFactory
{
public:
virtual AbstractProduct *MakePartA() = 0;
virtual AbstractProduct *MakePartB() = 0;
virtual AbstractProduct *MakePartC() = 0;
virtual AbstractProduct *AddPart(const AbstractProduct *pPart) = 0;
};
AbstractProduct *Client::CreateProduct(AbstractFactory &factory)
{
AbstractProduct *pProduct = factory.CreateProduct();
AbstractProduct *pPartA = factory.MakePartA();
AbstractProduct *pPartB = factory.MakePartB();
AbstractProduct *pPartC = factory.MakePartC();
factory.AddPart(pPartA);
factory.AddPart(pPartB);
factory.AddPart(pPartC);
return pProduct;
}
int main(void)
{
Client client;
ConcreteFactory factory;
client.CreateProduct(factory);
return 0;
}
3. Builder
意图:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
1) director提供抽象产品创建接口:如void Director::Construct();
2) 不同产品使用同一创建过程,由director指定特定builder以生产不同产品;
3) 接口内部实现了一个复杂对象(抽象产品)的创建:当传入具体工厂时,接口实现的是一个复杂的具体产品的创建;
4) 创建的产品并不立即返回,创建完毕后返回,或使用接口(GetProduct
)提取结果。
参与者:
• Builder
— 为创建一个Product对象的各个部件指定抽象接口。
• ConcreteBuilder
— 实现Builder的接口以构造和装配该产品的各个部件。
— 定义并明确它所创建的表示。
— 提供一个检索产品的接口。
• Director
— 构造一个使用Builder接口的对象。
• Product
— 表示被构造的复杂对象。ConcreteBuilder创建该产品的内部表示并定义它的装配过程。
— 包含定义组成部件的类,包括将这些部件装配成最终产品的接口。
代码:
class Builder
{
public:
virtual void MakePartA() = 0;
virtual void MakePartB() = 0;
virtual void MakePartC() = 0;
Product *GetProduct() { return _product; }
protected:
Product *_product;
};
class Director
{
public:
void setBuilder(Builder *b) { _builder = b; }
void Construct();
private:
Builder *_builder;
};
void Director::Construct()
{
_builder.MakePartA();
_builder.MakePartB();
_builder.MakePartC();
}
int main(void) {
ConcreteBuilderA concreteBuilderA;
ConcreteBuilderB concreteBuilderB;
Director director;
Product *pProduct;
director.SetBuilder(&concreteBuilderA);
director.Construct();
pProduct = concreteBuilderA.GetProduct();
pProduct->Show();
director.SetBuilder(&concreteBuilderB);
director.Construct();
pProduct = concreteBuilderB.GetProduct();
pProduct->Show();
return 0;
}
4. Factory Method
意图:定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method使一个类的实例化延迟到其子类。
1) 看得出该模式其实就是C++的多态特性,借继承实现。因此,其别名为虚构造器( Virtual Constructor);
2) 作为模式与C++多态特性不同的是,Creator可以定义工厂方法的缺省实现,完成缺省操作,MFC大量使用了这一思想。
参与者:
• Product
— 定义工厂方法所创建的对象的接口。
• ConcreteProduct
— 实现Product接口。
• Creator
— 声明工厂方法,该方法返回一个Product类型的对象。Creator也可以定义一个工厂方法的缺省实现,它返回一个缺省的ConcreteProduct对象。
— 可以调用工厂方法以创建一个Product对象。
• ConcreteCreator
— 重定义工厂方法以返回一个ConcreteProduct实例。
代码:
ConcreteProduct *ConcreteCreator::FactoryMethod()
{
ConcreteProduct
*pProduct = new ConcreteProduct
;
return pProduct;
}
Product *Creator::FactoryMethod()
{
Product *pProduct = new Product;
return pProduct;
}
int main(void) {
Creator creator;
ConcreteProduct *pProduct;
pProduct = creator.FactoryMethod();
pProduct->Show();
return 0;
}
5. Prototype
意图:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
1) 创建不再通过工厂新类继承(inheritance),而是通过委托(delegation);
2) 根通拷贝原型实例创建新对象。
参与者:
• ProtoType
— 声明一个克隆自身的接口。
• ConcreteProtoType
— 实现一个克隆自身的操作。
• Client
— 让一个原型克隆自身从而创建一个新的对象。
代码:
class ProtoType
{
public:
virtual void Draw();
virtual ProtoType *Clone() = 0;
virtual void Initialize();
};
class ProtoTypeA: public ProtoType
{
public:
virtual ProtoType *Clone()
{
return new ProtoTypeA;
}
};
class ProtoTypeB: public ProtoType
{
public:
virtual ProtoType *Clone()
{
return new ProtoTypeB;
}
};
class Client
{
public:
static ProtoType *Clone( int choice );
private:
static ProtoType *s_prototypes[3];
};
ProtoType* Client::s_prototypes[] = { 0, new ProtoTypeA, new ProtoTypeB };
ProtoType *Client::Clone(int choice)
{
return s_prototypes[choice]->Clone();
}
6. Singleton
意图:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
1) 用静态成员函数保证上述意图。
参与者:
• Singleton
— 定义一个Instance操作,允许客户访问它的唯一实例。Instance是一个类操作(即C++中的一个静态成员函数)。
— 可能负责创建它自己的唯一实例。
代码:
class Singleton
{
public:
static Singleton *GetInstance()
{
if (!s_instance)
s_instance = new Singleton;
return s_instance;
}
void Run() {}
private:
static Singleton *s_instance;
Singleton() {} // Singleton cannot be created outside.
};
Singleton *GetSingleton(void)
{
return Singleton::GetInstance();
}
int main(void)
{
GetSingleton()->Run();
return 0;
}
______________________________________________
代码写的都比较简单,基本可以将各种模式之间的不同体现出来了。