AbstarctFactory模式——设计模式学习
Abstract Factory 模式
一 意图
提供一个创建一系列相关或相互依赖对象的接口,而无需制定他们具体的类。
二 逻辑
抽象工厂模式中的4中不同角色:
1 抽象工厂(Abstract Factory):
它与应用系统商业逻辑无关。
2 具体工厂(Concrete Factory):
这个角色直接在客户端的调用下创建产品的实例。
这个角色含有选择合适的产品对象的逻辑,而这个逻辑是与应用系统的商业逻辑紧密相关的。
3 抽象产品(Abstract Product):
担任这个角色的类是工厂方法模式所创建的对象的父类,或它们共同拥有的接口。
4 具体产品(Concrete Product):
抽象工厂模式所创建的任何产品对象都是某一个具体产品类的实例。
这是客户端最终需要的东西,其内部一定充满了应用系统的商业逻辑。
三 结构图
此图我是看了很久,感觉非常困惑:
抽象工厂的意图是提供一个创建一系列相关或相互依赖对象的接口,
而无需制定他们具体的类。
根据图中所描述的。
问题:
1 一个接口:是指AbstractFctory还是所提供的方法:
CreateProductA(), CreateProductB()(这两个接口)
2 创建一系列对象:
CreateProductA(), CreateProductB(),这一系列对象之间要有什么关系吗?
3 相关或相互依赖:图中并没有体现出来怎么相关或者依赖?
4 创建一系列对象怎么使用:
直接给Client单独使用还是组合之后给Client使用,并没有交代清楚。
图中为何Client指向AbstractProductA和AbstractProductB这两个类,
既然是创建一系列对象,这一系列对象应该和这两个类是什么关系?
5 无需制定他们具体的类:图中明显提到了ProdcutA1和ProductB1等?
6 AbstractFactory是抽象类,是否所有的方法都是纯虚函数?
AbstractFactory类提供了全面的一系列创建对象的方法,它的派生类ConcreateFactory1,
是否是需要将AbstractFactory类的所有方法实现?(如果都是纯虚函数,那必然是)
7 AbstractFactory提供了创建对象 obj1,obj2,obj3,obj4,……等方法(非纯虚的),
ConcreateFactory1实现其中的obj1,obj2,obj3创建方法,
ConcreateFactory2实现了其中的obj2,obj3,obj4创建方法,这样的工厂是否有必要存在?
看了之后所给的maze游戏的maze创建方法之后才有所明白有些问题。
想法:
1 一个接口:AbstractFctory抽象类
2 CreateProductA(), CreateProductB()必然是相关或者相互依赖的关系对象,需要被组合起来使用。
3 相关或相互依赖:Client使用接口创建一系列对象之后,自己确定这对象之间的应用关系。
4 创建一系列对象怎么使用:由Client确定如何使用。
创建一系列相关或相互依赖的对象之后,利用其之间的关系需要将其组合成一个独立的对象来使用(最好如此),
这样工厂创建的一系列对象具有完整性,可以很好的再被封装起来。
5 无需制定他们具体的类:
在具体的工厂类提供的创建对象的方法ConcreateProductA()中制定具体的产品类的,
而对于Client而言,是不能知道的,仅知道抽象的产品产品类。
6 AbstractFactory作为抽象类,
提供了创建对象的统一的一系列方法,都是同一个层次上,
是不能确定那一个为纯虚函数而另一个不是纯虚函数,
所以AbstractFactory所有方法必然都纯虚函数(理论上认为)。
7 既然都是纯虚函数,都必须实现所有的方法。
如果只需要其中某几个对象,那么可以在Client创建需要的对象。
这样看似更加灵活,实则违背了AbstractFactory模式中:创建一系列相关或相互依赖对象。
创建的一系列对象尽量使其具有相关或相依赖。但是这在实际情况中是有需求的。
8 解决上述问题的方法之一:
将工厂AbstractFactory不必定义为抽象类,
将构造函数属性设为private,同样是不可以创建对象的,
而其派生类可以根据合适的需要来实现相应的方法。这算是AbstactFactory的一个变种。
为什么必然是相关或者相互依赖?
Client使用一个接口创建一系列对象,而如果这些对象之间的关联性很小,
那么就不会抽象这样一个工厂类,就不可能产生这样一个模式,违反类的设计原则。
相关或相互依赖的对象应该具体是怎么样的一种相关或相依赖?
具体要怎么一种相关这个我感觉不大容易说清楚。相关相依赖本身就是比较抽象的说法,只能具体问题具体对待。
在书中所设计maze游戏中,创建一系列对象:Maze,Wall,Room,Door等,之间关系是非常的密切。
四 代码实现
1 产品类
基类 Frame,派生类Title,Menu,Toolbar,Page及其各自相关的派生类
/***********************************************
* Class Frame *
**********************************************/
class Frame
{
public:
virtual void draw() = 0;
};
/***********************************************
* Class Title *
**********************************************/
class Title : public Frame
{
public:
virtual void draw()
{
cout<<"title draw"<<endl;
}
};
class TextTitle: public Title
{
public:
virtual void draw()
{
cout<<"TextTitle draw"<<endl;
}
};
class ImageTitle: public Title
{
public:
virtual void draw()
{
cout<<"ImageTitle draw"<<endl;
}
};
/***********************************************
* Class Menu *
**********************************************/
class Menu : public Frame
{
public:
virtual void draw()
{
cout<<"menu draw"<<endl;
}
};
class ListMenu: public Menu
{
public:
virtual void draw()
{
cout<<"ListMenu draw"<<endl;
}
};
class ThreeDMenu: public Menu
{
public:
virtual void draw()
{
cout<<"3DMenu draw"<<endl;
}
};
/***********************************************
* Class Toolbar *
**********************************************/
class Toolbar : public Frame
{
public:
virtual void draw()
{
cout<<"Toolbar draw"<<endl;
}
};
class CellToolbar : public Toolbar
{
public:
virtual void draw()
{
cout<<"CellToolbar draw"<<endl;
}
};
class FloatToolbar : public Toolbar
{
public:
virtual void draw()
{
cout<<"CellToolbar draw"<<endl;
}
};
/***********************************************
* 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 工厂类
抽象基类ControlFactory 派生类GeneralControl,DazzlingControl
/***********************************************
* Class controlFactory *
**********************************************/
class ControlFactory
{
public:
virtual Title* CreateTitle() = 0;
virtual Menu* CreateMenu() = 0;
virtual Toolbar* CreateToolBar() = 0;
virtual Page* CreatePage() = 0;
};
class GeneralControl:ControlFactory
{
public:
virtual Title* CreateTitle()
{
return new TextTitle();
}
virtual Menu* CreateMenu()
{
return new ListMenu();
}
virtual Toolbar* CreateToolBar()
{
return new CellToolbar();
}
virtual Page* CreatePage()
{
return new SlidePage();
}
};
class DazzlingControl:ControlFactory
{
public:
virtual Title* CreateTitle()
{
return new ImageTitle();
}
virtual Menu* CreateMenu()
{
return new ThreeDMenu();
}
virtual Toolbar* CreateToolBar()
{
return new FloatToolbar();
}
virtual Page* CreatePage()
{
return new VaryPage();
}
};
3 Client
bool ShowPage(Page* pg)
{
pg->draw();
return true;
}
int main()
{
//创建ControlFactory控件工厂类
ControlFactory* factory = (ControlFactory*) new GeneralControl();
//创建一系列相关的控件
Page* pg = factory->CreatePage();
Title* tl = (Title*)factory->CreateTitle();
Menu* mu = (Menu*)factory->CreateMenu();
Toolbar* tb = (Toolbar*)factory->CreateToolBar();
//将控件加入到Page中
pg->AddFrame((Frame*)tl);
pg->AddFrame((Frame*)mu);
pg->AddFrame((Frame*)tb);
//显示Page
ShowPage(pg);
return 0;
}
4 输出结果
page draw
TextTitle draw
ListMenu draw
CellToolbar draw
SlidePage draw
五 实例分析
工作之中其实发现对这个模式的应用并不多(和工作内容可能有些关系),
在一个系列的产品中各个组成构件基本上一致,
或者之间存在着构件之间的很大差异性,你有的我没有我有别的。
下面看几个实例:
对于VappMsgDialogCell不同系列的产品,其中的差别是比较小的而且又相似,
仅需要更改其中的某个就可以了,而大多数情况都是如此类型的。
如果使用AbstactFactory模式,对于一个很小差别却要重新创建所有的一系列产品,
增加好几个类,容易使代码变得冗余,不利于代码的重用。
但是对于AbstactFactory模式我感觉良好,它能保证我一次性关注的东西降到最少而且纯粹,
因为相对于代码重用,我更喜欢分开搞,
很多东西放在一起容易出错逻辑复杂,
一次性需要关注的东西太多,脑袋忙不过来(智商太低)。
但是在这个重构,复用满天飞的环境里面,这种想法只能是必须抛弃的。
六 分析总结
优点:
1 封装了具体类的实现
AbstarctFactory工厂类封装了产品的创建过程,
使Client不必去关心具体产品对象的创建实现过程。
2 具有整体的一致性
使用任何一个具体的工厂类,创建的是一系列相关或者相互依赖的产品对象,
客户端关注一些列相关的对象,降低复杂性,一次应用是一个整体。
3 灵活便于更改
一次应用创建的是一系列特定的相关的产品对象,当需要更换产品时,
只需要更改具体的创建工厂,改变变得很容易和迅速。
缺点:
1 不宜扩展
当一些列产品中某个组成构件需要变化时,
则需要重新设计新的产品类和创建新的工厂类。
当需要增加系列对象的组成构件时则需要更改AbstactFactory所有相关的类。
2 Client处理复杂
Client面对的是一些列的对象,
需要自己去合理的处理这些对象之间的关系,
而不是作为一个完整封装起来的整体对象。
重点:
1 系列产品的设计
确定相对稳定系列相关的产品和创建工厂类,
而且系列对象具有整体的可变性,封装的变化以达到产品迅速替换。
2 产品之间关系的确定
产品之间必然存在着相关或者相互依赖的关系,
而且显式的体现出来,使用工厂类创建一系列对象,
对象之间关系既要本身独立又要在某方面关联性很强。