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();
}
上课内容摘要
A. 工厂模式(Factory Method)
动机在软件系统中,经常面临着创建对象的工作;由于需求的变化,需要创建的对象的具体类型经常变化。 如何应对这种变化? 如何绕过常规的对象创建方法(new),提供一种“封装机制”来避免客户程序和这种“具体对象创建工作”的紧耦合?
模式定义
结构
要点总结
B. 抽象工厂(Abstract Factory)
动机在软件系统中,经常面临着“一系列相互依赖的对象”的创建工作;同时,由于需求的变化,往往存在更多系列对象的创建工作。 如何应对这种变化? 如何绕过常规的对象创建方法(new),提供一种“封装机制”来避免客户程序和这种“多系列具体对象创建工作”的紧耦合?
模式定义
结构
要点总结
C. 原型模式(Prototype)
动机在软件系统中,经常面临着“某些结构复杂的对象”的创建工作;由于需求的变化,这些对象经常面临着剧烈的变化,但是它们却拥有比较稳定一致的接口。 如何应对这种变化? 如何向“客户程序(使用这些对象的程序)”隔离出“这些易变对象”,从而使得“依赖这些易变对象的客户程序”不随着需求改变而改变?
模式定义
结构
要点总结
D. 构建器(Builder)
动机在软件系统中,有时候面临着“一个复杂对象”的创建工作其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是它们组合在一起的算法却相对稳定。 如何应对这种变化? 如何提供一种“封装机制”来隔离出“复杂对象的各个部分”的变化,从而保持系统中的“稳定构建算法”不随着需求改变而改变?
模式定义
结构
要点总结
“接口隔离”模式
在组件构建过程中,某些接口之间直接的依赖常常会带来很多问题,甚至根本无法实现。 采用添加一层间接(稳定)接口,来隔离本来互相紧密关联的接口是一种常见的解决方案。
A. 门面模式(Facade)
动机组件的客户和组件中各种复杂的子系统有了过多的耦合,随着外部客户程序和各子系统的演化,这种过多的耦合面临很多变化的挑战。 如何简化外部客户程序和系统间的交互接口? 如何将外部客户程序的演化和内部子系统的变化之间的依赖相互解耦?
模式定义
结构
要点总结
B. 代理模式(Proxy)
动机在面向对象系统中,有些对象由于某种原因(比如对象创建的开销很大,或者某些操作需要安全控制,或者需要进程外的访问等),直接访问会给使用者、或者系统结构带来很多麻烦。 如何在不失去透明要求操作对象的同时来管理/控制这些对象特有的复杂性? 增加一层间接层是软件开发中常见的解决方式。
模式定义
结构
要点总结
C. 适配器(Adapter)
动机在软件系统中,由于应用环境的变化,常常需要将“一些现存的对象”放在新的环境中应用,但是新环境要求的接口是这些现存对象所不满足的。 如何应对这种“迁移的变化”? 如何既能利用现有对象的良好实现,同时又能满足新的应用环境所要求的接口?
模式定义
结构
要点总结
D. 中介者(Mediator)
-动机在软件构建过程中,经常会出现多个对象互相关联交互的情况,对象之间常常会维持一种复杂的引用关系,如果遇到一些需求的更改,这种直接的引用关系将面临不断的变化。 这种情况下,我们可使用一个“中介对象”来管理对象间的关联关系,避免相互交互的对象之间的紧耦合引用关系,从而更好地抵御变化。
模式定义
结构