首页 > 代码库 > [GeekBand] 面向对象的设计模式(C++)(2)
[GeekBand] 面向对象的设计模式(C++)(2)
本篇笔记紧接上篇,继续学习设计模式。
4. 对象创建类设计模式
通过对象创建模式绕开new,来避免对象创建(new)过程中所导致的紧耦合,从而支持对象创建的稳定。它是接口抽象之后的第一步工作。
4.1 Factory Method(工厂方法)
4.1.1 应用场景
在软件系统中,经常面临着创建对象的工作;由于需求的变化,需要创建的对象的具体类型经常变化。
4.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中的例子:
class MainForm : public Form { SplitterFactory* factory;//工厂 public: MainForm(SplitterFactory* factory){ //利用传入参数来决定用什么文件分割器,这就把"变化"的范围限制在MainForm之外了 this->factory=factory; } ? ????void Button1_Click(){ ????ISplitter * splitter= factory->CreateSplitter(); //创造性的做出了一个多态的new splitter->split(); ????} }; |
4.1.3 核心
- Factory Method模式用于隔离类对象的使用者和具体类型之间的耦合关系。 面对一个经常变化的具体类型,紧耦合关系(new)会导致软件的脆弱。
- Factory Method模式通过面向对象的手法,将所要创建的具体对象工作延迟到子类,从而实现扩展(而非更改)的策略,较好地解决了这种紧耦合关系。
- Factory Method模式解决单个对象的需求变化。缺点在于要求创建方法/参数相同
4.1.4 类图
?
4.2 Abstract Factory(抽象工厂)
4.2.1 应用场景
在软件系统中,经常面临着创建"一系列相互依赖的对象"(和上边的唯一不同就是一系列相互依赖)的工作;由于需求的变化,需要创建的对象的具体类型经常变化。
4.2.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; ? }; |
在使用这个抽象的时候,如下面的例子:
class EmployeeDAO{ IDBFactory* dbFactory; ? public: vector<EmployeeDO> GetEmployees(){ IDBConnection* connection = dbFactory->CreateDBConnection(); connection->ConnectionString("..."); IDBCommand* command = dbFactory->CreateDBCommand(); command->CommandText("..."); command->SetConnection(connection); //关联性 ? IDBDataReader* reader = command->ExecuteReader(); //关联性 while (reader->Read()){ } } }; |
4.2.3 核心
- 如果没有应对"多系列对象构建" 的需求变化,则没有必要使用Abstract Factory模式,这时候使用简单的工厂完全可以。
- "多系列对象"指的是在某一特定系列下的对象之间(就是例子中的command,reader等)相互依赖或作用的关系。 不同系列的对象之间不能相互依赖。
- Abstract Factory模式主要在于应对 "新系列" 的需求变动。 真缺点在于难以应对 "新对象" 的需求变动。
4.2.4 类图
?
[GeekBand] 面向对象的设计模式(C++)(2)