GeekBand C++ 设计模式 第二周笔记

本文介绍了工厂方法模式和抽象工厂模式的应用场景与实现原理。工厂方法模式通过定义创建对象的接口,让子类决定实例化哪一个类,实现了解耦的目的。而抽象工厂模式则进一步提供了一个接口,用于创建一系列相关或相互依赖的对象,无需指定它们的具体类。

Factory Method(工厂方法)


1 应用场景

在软件系统中,经常面临着创建对象的工作;由于需求的变化,需要创建的对象的具体类型经常变化。

2 定义与解释

定义一个用于创建对象的接口,让子类决定具体实例化哪个类。Factory Method是的一个类的实例化延迟到子类。(目的是解耦,手段是虚函数)

考虑之前在学习观察者模式的文件分割器例子,通常来讲,我们很可能写出这样的代码:

BinarySplitter * splitter= new BinarySplitter();//依赖具体类
声明一个文件分割器的对象,接下来再使用它。但实际上这是不符合”面向接口编程”的,它直接的使用了BinarySplitter具体类进行编程。我们可能会有二进制分割器,文本分割器,视频分割器等等。那我们接下来就会想到,使用一个抽象类接口,然而这样只能部分地消除依赖:

ISplitter * splitter = //不再依赖具体类

new BinarySplitter();//仍然依赖具体类,BinarySplitter不存在时无法编译通过

在这种情况下,只要有依赖就仍然做不到解耦。而后边又无法搞成接口类,(接口类无法执行new操作)。这就引入了工厂方法,这是面向接口编程的第一步需求。

工厂方法就是用一个函数来代替new操作,这个函数要能够产生各种不同的分割器,我们就又想到了虚函数(虚函数和继承机制是延迟决定的唯一方法),如下图工厂方法的具体代码,不同的工厂产生不同的分割器:


class SplitterFactory{

public:

virtual ISplitter* CreateSplitter()=0;

virtual ~SplitterFactory(){}

};



//具体工厂

class BinarySplitterFactory: public SplitterFactory{

public:

virtual ISplitter* CreateSplitter(){

return new BinarySplitter();

}

};



class TxtSplitterFactory: public SplitterFactory{

public:

virtual ISplitter* CreateSplitter(){

return new TxtSplitter();

}

};

在使用这个分割器的时候,如下面的MFC中的例子:


{

SplitterFactory* factory;//工厂

public:

MainForm(SplitterFactory* factory){ //利用传入参数来决定用什么文件分割器,这就把"变化"的范围限制在MainForm之外了

this->factory=factory;

}


    void Button1_Click(){

    ISplitter * splitter=

factory->CreateSplitter(); //创造性的做出了一个多态的new

splitter->split();

    }

};

Abstract Factory(抽象工厂)


1 应用场景

在软件系统中,经常面临着创建”一系列相互依赖的对象”(和上边的唯一不同就是一系列相互依赖)的工作;由于需求的变化,需要创建的对象的具体类型经常变化。

2 定义与解释

提供一个接口,让该接口负责创建一系列”相关或相互依赖的对象” ,无需指定它们具体的类。

考虑在软件的层次架构中的数据访问层,需要访问数据库。现在使用的是SQL的数据库,但是以后有可能会使用其他种类的数据库比如Oracle等。我们的想法还是进行面向接口的编程,应用工厂方法之后我们不难写出如下的接口:

//数据库访问有关的基类

class IDBConnection{



};

class IDBConnectionFactory{

public:

virtual IDBConnection* CreateDBConnection()=0;

};

//支持SQL Server

class SqlConnection: public IDBConnection{



};

class SqlConnectionFactory:public IDBConnectionFactory{



};

//支持Oracle

class OracleConnection: public IDBConnection{



};

class OracleConnectionFactory: public IDBConnectionFactory {



};

但是这种情况下,用户需要手动搭配DBConnection,DataReader存在用户错误的把不同类型的操作拼到一起的问题。

因此我们希望能进一步进项抽象,提取这些工厂的特质,就产生了抽象工厂方法。其实更应该叫做”家族工厂”“工厂组”之类,它把不同的操作用同一个工厂类产生,防止了某些用户错误地把不同种类的操作混杂在一起(例如SQL的Command配上Oracle的Reader)。

class IDBFactory{

public:

virtual IDBConnection* CreateDBConnection()=0;

virtual IDBCommand* CreateDBCommand()=0;

virtual IDataReader* CreateDataReader()=0;



};



class SqlDBFactory:public IDBFactory{

public:

virtual IDBConnection* CreateDBConnection()=0;

virtual IDBCommand* CreateDBCommand()=0;

virtual IDataReader* CreateDataReader()=0;

};

Prototype(原型)


1 应用场景

在软件系统中,经常面临着创建”某些结构复杂的的对象”的工作;由于需求的变化,需要创建的对象的具体类型经常变化,但是他们却拥有比较比较一致的接口。

2 定义与解释

使用一个原型实例指定创建对象的种类,通过复制这个原型创建对象。

仍然考虑文件分割器,我们类比工厂方法。原型模式是一种特殊的创建,通过克隆(复制构造)来进行创建。

//抽象类

class ISplitter{

public:

virtual void split()=0;

virtual ISplitter* clone()=0; //通过克隆自己来创建对象



virtual ~ISplitter(){}



};

//具体类,直接利用拷贝构造函数进行配置

class BinarySplitter : public ISplitter{

public:

virtual ISplitter* clone(){

return new BinarySplitter(*this);

}

};

但是这种情况下,用户需要手动搭配DBConnection,DataReader存在用户错误的把不同类型的操作拼到一起的问题。

在使用这个抽象的时候,如下面的例子。必须强调原型对象尽管已经存在了,但是它不是用来更改的,只能用来复制。(否则不同的操作之间就可能产生影响,相当于原型有了初值。)

class MainForm : public Form

{

ISplitter* prototype;//原型对象



public:

MainForm(ISplitter* prototype){

this->prototype=prototype; //让原型对象指向传过来的原型

}

    void Button1_Click(){

    ISplitter * splitter=

prototype->clone(); //克隆原型

splitter->split();

}

};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值