Factory Method模式
1. 意图
定义一个用于创建对象的接口,让子类决定实例化哪一个类。
Factory Method使一个类的实例化延迟到其子类。
2. 动机
框架使用抽象类定义和维护对象之间的关系。这些对象的创建通常也由框架负责。
在多文档框架中,两个主要的抽象是类Application和Document。
客户必须通过它们的子类来做与具体应用相关的实现。
例如,为创建一个绘图应用,我们定义类DrawingApplication和DrawingDocument。
Application类负责管理Document并根据需要创建它们—例如,当用户从菜单中选择Open或New的时候。
因为被实例化的特定Document子类是与特定应用相关的,
所以Application类不可能预测到哪个Document子类将被实例化
Application类仅知道一个新的文档何时应被创建,而不知道哪一种Document将被创建。
这就产生了一个尴尬的局面:框架必须实例化类,但是它只知道不能被实例化的抽象类。
Factory Method模式提供了一个解决办案。它封装了哪一个Document子类将被创建的信息并将这些信息从该框架中分离出来
如下页上图所示。
Application的子类重定义Application的抽象操作CreateDocument以返回适当的Document子类对象。
一旦一个Application子类实例化以后,它就可以实例化与应用相关的文档,而无需知道这些文档的类。
我们称CreateDocument是一个工厂方法(FactoryMethod),因为它负责“生产”一个对象。
这里似乎说的有点不妥……实例化了Application子类之后,还要实例化应用相关文档,这个由谁实例?
由Application子类,他如果不知道文档类,如何去实例化???除非此处在通过封装,那也需要相应参数。
4 结构图
看到这里我有点晕了!我一时间感觉我已经分不清楚
FactoryMethod和 AbstractFactory及Builder这之间的区别了……
1 都提供有创建对象的接口:
Creator有FactoryMethod(), Builder有BuilderPart();Abstarct有CreateProduct()
2 都可以组装一些列对象:
Creator有FactoryMethod()直接创建组装,Builder由Director组装,Abstarct由Client组装。
3 都具有相似的结构
所不同的是:Builder有一个Director,Abstarct有Client,但这个可以给Creator增加一个Client之类的,使其都具有相似的结构。
(具体的差别后续学完所有创建型模式后总结)
FactoryMethod模式定义:
定义一个用于创建对象的接口,让子类决定实例化哪一个类。FactoryMethod使一个类的实例化延迟到其子类。
使劲想了很久,这下有点明白了!
FactoryMethod仅是提供一个接口一个方法:FactoryMethod用来决定实例化哪一个类对象!
五 代码实现
两种实现:
1独立创建:
(1) Creator是一个抽象类。所有实现都必须要子类来实现,需要考虑到实例化所有情况的类。
(2) Creator是个具体的类提供了创建对象的缺省实现的虚函数。如果有需求则派生一个子类。
这样是用一个独立的操作来创建对象,保证每一个产品的创建过程都是独立的。
两种情况其实并没有太大改善,都能够保证每一个产品创建过程之间绝对的独立。
但是我觉得这样的方法对于简单的产品创建不会太常用,
a 一个产品往往需要改变的不是全部特性,而只是其中某一小部分特性。但是都需要增加一个类和改变产品创建处的产品创建Creator类。
b 因为总要传入一个创建特定产品的ConcreteCreator对象,还是有一些硬编码,还会造成大量代码的冗余。
c 对于产品之间具有极大的差别的对象的创建可能这样比较好,应该是在Client
使用的时候。但对于框架来说这是不利于扩展的,那么需要扩展的是什么。
<当然活学活用,这又让我想到 创建型模式由来解决对象创建的问题,那么什么样的对象创建问题呢?
并不是所有的有创建对象的时候都需要使用这些模式,如果这样那就没完没了没有意义了,那么到底是什么呢,探索中>
2 参数化
在FactoryMethod中增加一个参数,这样可以创建多种产品(同一类型)。
这个应该是比较常用的,各类代码中随处可见的,具有很好的扩展性!
3 模板化
使用一个Creator的模板,可以减少Creator派生类的创建。只需要传不同的Podduct。
六 具体代码实现
1 product实现
/**********************************************************************
* Product *
*********************************************************************/
/***********************************************
* Class Frame *
**********************************************/
class Frame
{
public:
virtual void draw() = 0;
};
/***********************************************
* Class Page *
**********************************************/
class Page : public Frame
{
public:
#define FRAME_MAX 10
Page()
{
m_frame_num = 0;
}
void AddFrame(Frame* frm)
{
if (m_frame_num < FRAME_MAX)
{
m_frame[m_frame_num] = frm;
m_frame_num++;
}
}
virtual void draw()
{
cout<<"page draw"<<endl;
for (int i =0; i < m_frame_num; i++)
{
m_frame[i]->draw();
}
}
private:
Frame* m_frame[FRAME_MAX];
int m_frame_num;
};
class SlidePage : public Page
{
public:
virtual void draw()
{
Page::draw();
cout<<"SlidePage draw"<<endl;
}
};
class VaryPage : public Page
{
public:
virtual void draw()
{
Page::draw();
cout<<"VaryPage draw"<<endl;
}
};
2 Creator
/**********************************************************************
* Creator *
*********************************************************************/
/***********************************************
* Class Creator *
**********************************************/
class PageCreator
{
virtual Page* CreatePage() = 0;
};
class GeneralPageCreator: public PageCreator
{
public:
virtual Page* CreatePage()
{
return new SlidePage();
}
};
class SpecialPageCreator: public PageCreator
{
public:
virtual Page* CreatePage()
{
return new VaryPage();
}
};
class CommonPageCreator: public PageCreator
{
public:
virtual Page* CreatePage(){return NULL;}
virtual Page* CreatePage(int type)
{
switch(type)
{
case 1:
{
return new SlidePage();
break;
}
case 2:
{
return new VaryPage();
break;
}
default:
//ASSERT(0);
break;
}
return NULL;
}
};
template <class ConcreatePage>
class TemplatePageCreator: public PageCreator
{
public:
virtual Page* CreatePage()
{
return new ConcreatePage();
}
};
3 Client
/**********************************************************************
* Client *
*********************************************************************/
int main()
{
// 派生类创建
GeneralPageCreator gCreator;
Page* pg1 = gCreator.CreatePage();
pg1->draw();
SpecialPageCreator SCreator;
Page* pg2 = SCreator.CreatePage();
pg2->draw();
//参数化方法
CommonPageCreator CCreator;
Page* pg3 = CCreator.CreatePage(1);
pg3->draw();
Page* pg4 = CCreator.CreatePage(2);
pg4->draw();
//模板方法
TemplatePageCreator<SlidePage>TCreator1;
Page* pg5 = TCreator1.CreatePage();
pg5->draw();
TemplatePageCreator<VaryPage>TCreator2;
Page* pg6 = TCreator2.CreatePage();
pg6->draw();
return 0;
}
各种方法的不同可以从代码中清晰的看出!
七 其他应用
1 为子类提供挂钩
父类为子类提供缺省对象创建的实现。
Document中创建FileDialog两种方式:
(1) 类中创建,父类提供一个缺省的创建Dialog的方法
(3) 类外创建 由客户端自行创建
2 连接平行的类层次
平行的类层次:当一个类将它的一些职责委托给一个独立的类的时候。
如果将所有的图形操作都放到图形对象中:
(1) 图形类将会很臃肿,很多信息只是在操作的时需要。
(2) 不便于扩展和代码重用。不同图形具有的操作是不一样的。
需要将对图形的操作进行封装,作为一个独立的对象实现交互。
需要注意的是:工厂方法怎样定义两个类层次之间的连接的,它将哪些类应一同工作的信息局部化了。
<图形创建操作类对象,返回给Client使用 >
另一种方式:
<Client创建委托的类对象VcpTextStorage对象,指定给VcpTextBasicLayout对象>
八 实例分析
创建不同信息的显示
八 总结
1 应用场景:
1 提供对象创建接口
2 将对象的创建延迟到子类
3 为子类提供挂钩
4 连接平行类层次
2 实现方式:
1 继承派生:不同产品不同Creator
2 参数化: 对FactoryMethod方法增加参数标识需要创建的产品
3 模板: 创建模板类Creator