【转载】Abstract Factory Step by Step --- 抽象工厂
抽象工厂是创建型模式的代表,其他的还有单件(Singleton)、生成器(Builder)、工厂方法(Factory Method)以及原型(Prototype),模式本身没有好坏之分,只有适用不适用的区别。
最近常看喜洋洋与灰太狼,这是发生在青青草原的故事,其中涉及的动物有绵羊、山羊、羚羊、狼族等,本文就以创建绵羊(Sheep)和狼(Wolf)为例来说明Abstract Factory的使用方法。对于绵羊(Sheep),它由绵羊头(SheepHead)、绵羊身体(SheepBody)组成、具有白色的毛皮(SheepFur),而狼(Wolf)是由狼头(WolfHead)、狼身(WolfBody)组成、具有黑色的狼毛(WolfFur)。现在我们要创建一个喜洋洋和一个灰太狼,让我们先闭着眼睛思考一下看我们的大脑会给我们一个什么样的反馈??我的大脑反馈如下图所示:
图 1
是的,实现这个设计很容易,任何一个学过C++、C#或Java等对象语言的程序员都可以很快搞定它,但是在实现这个设计之前让我们先看看这个设计有没有缺陷。可能你也发现了:一、这个设计没有丝毫可扩展性,既然我们是在为青青草原服务,那么如果客户要我们要创建山羊(Goat)呢、要创建老虎呢(Tiger)呢?? 对、我们又得增加一堆的类来处理这些新的请求,没有扩张性的软件是缺乏生命力的;第二、上面的设计中似乎有很多相似的类:SheepHead和WolfHead,SheepBody和WolfBody,SheepFur和WolfFur,是抽象上场的时候了,我们可以尝试着把相似的类用父类统一起来,改进后的设计如下图:
(在实现之前检查(Review)你的设计可以极大的提高代码的质量并减少返工的次数及工作量,检查并改善当前的设计可能要迭代很多次,邀请对此功能比较熟悉的人一起帮你检查往往会有很好的效果)
图 2
在上面这个图是对图1的一次改进,如果我们仅仅是在脑子里或者直接在code里做这一步也许我们会觉得这个设计已经修改完毕了,但是当用UML图表示出来的时候我们会明显的发现这里有问题:对、就是Sheep类和Wolf类可以接触对具体的实现类的依赖、而是去依赖相应的接口类,如下图所示:
图 3
继续观察上图3我们又有了新的发现,那就是类Sheep和Wolf除了名字不一样之外其他的一模一样(现实的项目中未必如此,但只要能提取出符合条件的因素就行),OO的特性告诉我们应该对这两个类进行抽象,于是我们可以得到进一步改进的设计图如下:
图 4
这里我们已经能体会到面向对象的一大特性了:抽象。至此我们先满足下我们的客户--动画制作师的好奇心吧,去创建一个喜洋洋跟灰太狼出来,要不客户该着急了
class Head
{
public:
virtual ~Head() = 0; /** 申明该类为一个抽象类 **/
};
Head::~Head() /** 如果抽象类中只有析构函数、那么需要提供析构的定义 **/
{ /** 否则在使用的时候连接会出错 **/
}
class Body
{
public:
virtual ~Body() = 0;
};
Body::~Body()
{
}
class Fur
{
public:
virtual ~Fur() = 0;
}; Fur::~Fur()
{
}
//类SheepHead, SheepBody, SheepFur实现
class SheepHead : public Head
{
public:
SheepHead()
{
cout << "Sheep Head" << endl;
} virtual ~SheepHead(){}
}; class SheepBody : public Body
{
public:
SheepBody()
{
cout << "Sheep Body" << endl;
} virtual ~SheepBody(){}
}; class SheepFur : public Fur
{
public:
SheepFur()
{
cout << "Sheep Fur" << endl;
} virtual ~SheepFur(){}
};
//类Animal实现
class Animal
{
public:
Animal(Head* head, Body* body, Fur* fur)
:phead(head), pbody(body), pfur(fur)
{} virtual ~Animal()
{
memset(this, , sizeof(Animal));
} private:
Head* phead;
Body* pbody;
Fur* pfur;
};
int _tmain(int argc, _TCHAR* argv[])
{
auto_ptr<Head> sheepHead(new SheepHead()); /** 以对象管理资源 **/
auto_ptr<Body> sheepBody(new SheepBody());
auto_ptr<Fur> sheepFur(new SheepFur());
auto_ptr<Animal> xiyangyang(new Animal(sheepHead.get(), sheepBody.get(), sheepFur.get()));
return 0;
}
不得不说,C/C++写代码就是麻烦,Java/C#几句就搞定的C++得弄半天。言规正传,让我们看看运行的结果吧
如你所愿,输出正是我们想要的结果,这个设计看起来还不错,我们在其中应用了一些OO的基本概念:抽象、封装、继承,只要我们再实现了创建狼族的部分就可以提交了。现在如果我们要创建灰太狼的话客户(Client)就需要如下实现: (对Animal类来说,它的客户就是main函数)
//创建灰太狼的main
int _tmain(int argc, _TCHAR* argv[])
{
auto_ptr<Head> head(new WolfHead());
auto_ptr<Body> body(new WolfBody());
auto_ptr<Fur> fur(new WolfFur());
auto_ptr<Animal> huitailang(new Animal(head.get(), body.get(), fur.get()));
return 0;
}
一切看起来还好,这段代码创建一个灰太狼,并且运行良好,但是我们总觉得它有些地方不那么舒服,对了,就是红色标识的那部分(在此不考虑delete指针的部分),看起来客户main函数要创建一个“喜羊羊”和一个“灰太狼”的操作高度相似,《重构》告诉我们如果发现我们的代码里有重复的地方,那么这里也许就是要重构的地方,试想想,如果客户main函数还要创建刀羊前辈(Goat)、创建泰哥(Tiger)等等,那么它就得做很多重复的高度相似的工作,那么这个函数肯定会很难看,更要命的是维护起来也费劲,万一将来有改动就容易出Bug,比如我们修改了Head中的某一个方法,那么客户main函数就得相应的发生变化,代码间的耦合比较大。我们有没有办法提供一种机制,让客户只需要发送一个指令告诉它想要获得一个动物然后系统就能自动将其创造出来呢?肯定有,可以将创建动物的工作用一个类封装起来,然后客户只需要通过该类的一次调用就可以完成所有的工作,请看下图
图 5
现在客户(main)请求动物Animal类给他创建对象的时候指定一个专门生成该对象的类SheepFactory,然后将具体的创建委托给Animal和SheepFactory,这样就实现了客户跟具体的动物部件的解藕,因为客户已经不再知道Sheep是由头、身体和皮毛组成的了,它只是知道它要让Animal从SheepFactory里制作一个“喜羊羊”出来。下面是大概的code
class SheepFactory
{
public:
Head* CreateHead()
{
return new SheepHead();
}
Body* CreateBody()
{
return new SheepBody();
}
Fur* CreateFur()
{
return new SheepFur();
}
};
改进后的Animal类
class Animal /* Base class of the animal on "Qingqing grassland" */
{
public:
Animal(SheepFactory& factory)
: phead(factory.CreateHead()),
pbody(factory.CreateBody()),
pfur(factory.CreateFur())
{
}
virtual ~Animal() {}
private:
auto_ptr<Head> phead;
auto_ptr<Body> pbody;
auto_ptr<Fur> pfur;
};
int _tmain(int argc, _TCHAR* argv[])
{
SheepFactory sheep;
Animal xiyangyang(sheep);
return 0;
}
现在我们看到客户main函数里已经看不到任何Head、SheepHead等字眼了,解藕成功 ^_^ ,并且客户的代码变得非常简洁,如果现在要创建“灰太狼”,也许你已经想到了,我们需要提供一个专门生产狼的工厂类WolfFactory来供Animal使用,同时还需要扩展类Animal的构造函数,以使之支持WolfFacotry类型的参数(请注意这句话,读完这篇文章之后认真看这句话所指的代码你就会对面向接口编程和面向实现编程有一定的区分能力),
扩展后的Animal
class Animal /* Base class of the animal on "Qingqinggrassland" */
{
public:
Animal(SheepFactory& factory)
{
phead = factory.CreateHead();
pbody = factory.CreateBody();
pfur = factory.CreateFur();
} Animal(WolfFactory& factory) /* we have to add these codes to support the WolfFactory param */
{
phead = factory.CreateHead();
pbody = factory.CreateBody();
pfur = factory.CreateFur();
} /* 其他部分相同 */
};
int _tmain(int argc, _TCHAR* argv[])
{
SheepFactory sheep;
Animal xiyangyang(sheep); WolfFactory wolf;
Animal huitailang(wolf); return ;
}
现在这个设计已经比较理想了,在这个例子里我们已经使用了一些OO的概念:抽象、继承、封装,并且给客户提供的接口简洁好用,但是如果我们再细心一点就会发现,现在的main里有两个高度相似的具体类/实现类SheepFactory和WolfFactory,这暗示我们这个设计还需要改进,OO软件设计的原则之一就是“面向接口编程,而非面向实现编程”,因此我们得到了如下的UML图
图 6
对应修改的code如下
2 {
3 public:
4 virtual Head* CreateHead() = 0;
5 virtual Body* CreateBody() = 0;
6 virtual Fur* CreateFur() = 0;
7
8 virtual ~AnimalFactory(){};
9 };
2 /* 其他部分未变 */
现在的Animal
2 {
3 public:
4 Animal(AnimalFactory& factory) /* Please pay more attetion in here, notice the difference with the previous */
5 {
6 phead = factory.CreateHead();
7 pbody = factory.CreateBody();
8 pfur = factory.CreateFur();
9 }
10
11 virtual ~Animal()
12 {
13 }
14
15 private:
16 auto_ptr<Head> phead;
17 auto_ptr<Body> pbody;
18 auto_ptr<Fur> pfur;
19 };
2 {
3 auto_ptr<AnimalFactory> factory(SheepFactory());
4 Animal xiyangyang(*factory);
6
7 return 0;
8 }
如果我们需要创建“灰太狼”,则只需要 factory = new WolfFactory(); 然后将该factory传送给Animal,而无需再扩展Animal的构造函数,怎么样,现在你能体会到一点面向接口编程的味道了吗,至少它带来的好处是显而易见的。
到目前为止,你已经从头到尾的学了一遍Abstract Factory(抽象工厂)模式,现在就让我们来看看四人组对Abstract Factory的描述
1、意图:提供一个一些列提供相关或相互依赖对象的接口,而无需指定他们的具体类
2、结构图:
图 8
3、适用性等其他内容请参考四人组的《设计模式》,此处不再拷贝粘贴
注意,上图8里的Client其实就是本文所讲的Animal类,而本文中所说的Client是指main函数。最后是本例中的C++源码,谨供参考
#include <iostream>
#include <memory> using namespace std; /**
* Foward declaration
*/
class Head;
class Body;
class Fur; /**
* Interfaces declaration
*/
class Head
{
public:
virtual ~Head() = ; /* indicates that this class is an interface class */
}; /**
* different with nomal interface method, to declare a C++ interface
* class with only one destructor, then we must define this method,
* pls see the Chap.14 of <<effective C++>> for reference
*/
Head::~Head()
{
} class Body
{
public:
virtual ~Body() = ;
}; Body::~Body()
{
} class Fur
{
public:
virtual ~Fur() = ;
}; Fur::~Fur()
{
} /**
* Sheep concrete class definition
*/
class SheepHead : public Head
{
public:
SheepHead()
{
cout << "Sheep Head" << endl;
} virtual ~SheepHead(){}
}; class SheepBody : public Body
{
public:
SheepBody()
{
cout << "Sheep Body" << endl;
} virtual ~SheepBody(){}
}; class SheepFur : public Fur
{
public:
SheepFur()
{
cout << "Sheep Fur" << endl;
} virtual ~SheepFur(){}
}; /**
* Wolf concrete class definition
*/
class WolfHead : public Head
{
public:
WolfHead()
{
cout << "Wolf Head" << endl;
} virtual ~WolfHead(){}
}; class WolfBody : public Body
{
public:
WolfBody()
{
cout << "Wolf Body" << endl;
} virtual ~WolfBody(){}
}; class WolfFur : public Fur
{
public:
WolfFur()
{
cout << "Wolf Fur" << endl;
} virtual ~WolfFur(){}
}; /**
* Factory interface declare and Concrete factory definition
*/
class AnimalFactory
{
public:
virtual Head* CreateHead() = ;
virtual Body* CreateBody() = ;
virtual Fur* CreateFur() = ; virtual ~AnimalFactory(){};
}; class SheepFactory : public AnimalFactory
{
public:
virtual Head* CreateHead()
{
return new SheepHead();
}
virtual Body* CreateBody()
{
return new SheepBody();
}
virtual Fur* CreateFur()
{
return new SheepFur();
}
}; class WolfFactory : public AnimalFactory
{
public:
virtual Head* CreateHead()
{
return new WolfHead();
}
virtual Body* CreateBody()
{
return new WolfBody();
}
virtual Fur* CreateFur()
{
return new WolfFur();
}
}; class Animal /* Base class of the animal on "Qingqing grassland" */
{
public:
Animal(AnimalFactory& factory)
: phead(factory.CreateHead()),
pbody(factory.CreateBody()),
pfur(factory.CreateFur())
{
} virtual ~Animal(){} private:
auto_ptr<Head> phead;
auto_ptr<Body> pbody;
auto_ptr<Fur> pfur;
}; int _tmain(int argc, _TCHAR* argv[])
{
auto_ptr<AnimalFactory>sheep(new SheepFactory());
Animal xiyangyang(*sheep);
auto_ptr<AnimalFactory>wolf(new WolfFactory());
Animal huitailang(*wolf); return ;
}
运行结果如下:
【转载】Abstract Factory Step by Step --- 抽象工厂的更多相关文章
- 工厂方法模式(Factory Method)和抽象工厂模式(Abstact Factory)
分类 工厂模式主要是为创建对象提供过渡接口,以便将创建对象的具体过程屏蔽隔离起来,达到提高灵活性的目的.工厂模式在<Java 与模式>中分为三类:1)简单工厂模式(Simple Facto ...
- 设计模式——抽象工厂模式
Abstract Factory模式,即抽象工厂模式,该模式要求Factory类为抽象的,并且里面的生产出来的产品也是抽象的! 这里需要着重说的一点就是,抽象类和抽象方法的作用--规范了子类的实现,以 ...
- Simple Factory vs. Factory Method vs. Abstract Factory【简单工厂,工厂方法以及抽象工厂的比较】
I ran into a question on stackoverflow the other day that sort of shocked me. It was a piece of code ...
- 设计模式之美:Abstract Factory(抽象工厂)
索引 别名 意图 结构 参与者 适用性 缺点 效果 相关模式 命名约定 实现 实现方式(一):使用 Factory Method 来实现 Abstract Factory. 实现方式(二):使用 Pr ...
- .NET设计模式(2):1.2 抽象工厂模式(Abstract Factory)
概述 抽象工厂模式(Abstract Factory)是所有形态的工厂模式中最为抽象和最具一般性的一种形态.抽象工厂模式是指当有多个抽象角色时,使用的一种工厂模式.抽象工厂模式可以向客户端提供一个接口 ...
- 设计模式(3)抽象工厂模式(Abstract Factory)
设计模式(0)简单工厂模式 设计模式(1)单例模式(Singleton) 设计模式(2)工厂方法模式(Factory Method) 源码地址 0 抽象工厂模式简介 0.0 抽象工厂模式定义 抽象工厂 ...
- 设计模式——抽象工厂(Abstract Factory)
提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们的具体类. ——DP UML类图 模式说明 抽象工厂与工厂方法在定义上最明显的区别是“创建一系列相关或相互依赖对象的接口”,由此可以看出抽象工 ...
- 一天一个设计模式——Abstract Factory抽象工厂模式
一.模式说明 前面学习了工厂方法(Factory Method)模式.在工厂方法模式中,在工厂方法模式中,父类决定如何生成实例,但并不决定所要生成的具体类,具体的处理交由子类来处理.这里学习的抽象工厂 ...
- 设计模式之抽象工厂模式(Abstract Factory Pattern)
一.抽象工厂模式的由来 抽象工厂模式,最开始是为了解决操作系统按钮和窗体风格,而产生的一种设计模式.例如:在windows系统中,我们要用windows设定的按钮和窗体,当我们切换Linux系统时,要 ...
- java设计模式2--抽象工厂模式(Abstract Factory)
本文地址:http://www.cnblogs.com/archimedes/p/java-abstract-factory-pattern.html,转载请注明源地址. 抽象工厂模式(别名:配套) ...
随机推荐
- 教你如何有效防止DDos攻击?
DDos又称分布式拒绝服务,DDos是利用大量合理的请求造成资源过载,导致服务不可用.就比如一个餐馆总共有100个座位,突然有一天某个商家恶意竞争,雇佣了200个人来到这个餐馆坐着不吃不喝,门口还排着 ...
- Nginx负载均衡与转发
1.6种负载均衡策略 1.轮询 :默认方式 2.weight : 权重方式 3.ip_hash :依据ip分配方式 4.least_conn :最少连接方式 5.fair(第三方) :响应时间方式 6 ...
- js的基本语法规范
1.不要在同一行声明多个变量: 2.使用===/!==来比较true/false的返回值: 3.使用字面量替代new Array这种形式: 4.不要使用全局函数: 5.switch语句必须带有defa ...
- 如何清除Windows共享登录的用户名密码
打开cmd 1.[查看已记录的登录信息] net use 2.[清除记录] 得关掉你所有打开的samba之后再 net use * /del
- B 树 B+树
拜读了 http://blog.csdn.net/v_july_v/article/details/6530142, 自己总结下: B树的出发点是为了解决磁盘IO慢的问题,尽量再一个磁盘块中提供更 ...
- php数组长度怎么获取
我们可以将元素添加到数组或从数组中删除元素,那么如果我们想要知道数组中存在的元素的总长度或总数,我们就可以使用count() 或sizeof函数. 下面我们就通过简单的示例,给大家介绍php获取数组长 ...
- Java语法清单-快速回顾(开发)
Java CheatSheet 01.基础 hello,world! public static void main(String[] args) { System.out.println(" ...
- 双目立体匹配经典算法之Semi-Global Matching(SGM)概述:代价聚合(Cost Aggregation)
由于代价计算步骤只考虑了局部的相关性,对噪声非常敏感,无法直接用来计算最优视差,所以SGM算法通过代价聚合步骤,使聚合后的代价值能够更准确的反应像素之间的相关性,如图1所示.聚合后的新的代价值保存 ...
- NX二次开发-UFUN将工程图中的点坐标映射到建模绝对坐标UF_VIEW_map_drawing_to_model
#include <uf.h> #include <uf_ui.h> #include <uf_draw.h> #include <uf_view.h> ...
- NX二次开发-UF_MODL_ask_distance_tolerance获取建模的长度公差
NX9+VS2012 #include <uf.h> #include <uf_modl.h> #include <uf_ui.h> UF_initialize() ...