博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【转载】Abstract Factory Step by Step --- 抽象工厂
阅读量:5336 次
发布时间:2019-06-15

本文共 9061 字,大约阅读时间需要 30 分钟。

抽象工厂是创建型模式的代表,其他的还有单件(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, 0, 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 0;}

现在这个设计已经比较理想了,在这个例子里我们已经使用了一些OO的概念:抽象、继承、封装,并且给客户提供的接口简洁好用,但是如果我们再细心一点就会发现,现在的main里有两个高度相似的具体类/实现类SheepFactory和WolfFactory,这暗示我们这个设计还需要改进,OO软件设计的原则之一就是“面向接口编程,而非面向实现编程”,因此我们得到了如下的UML图

                          

                                                                                  图 6

对应修改的code如下

复制代码
1 class AnimalFactory
2 {
3 public:
4     virtual Head* CreateHead() = 0;
5     virtual Body* CreateBody() = 0;
6     virtual Fur* CreateFur() = 0;
8     virtual ~AnimalFactory(){};
9 };
复制代码

 

1 class SheepFactory : public AnimalFactory
2 /* 其他部分未变 */

 

现在的Animal

复制代码
 1 class Animal                            /* Base class of the animal on "Qingqing grassland" */
 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 };
复制代码

 

复制代码
1 int _tmain(int argc, _TCHAR* argv[])
2 {
3     auto_ptr<AnimalFactory> factory(SheepFactory());
4     Animal xiyangyang(*factory);
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 
#include
using namespace std;/** * Foward declaration */class Head; class Body;class Fur;/*** Interfaces declaration*/class Head{public: virtual ~Head() = 0; /* 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 <
> for reference */Head::~Head(){}class Body{public: virtual ~Body() = 0;};Body::~Body(){}class Fur{public: virtual ~Fur() = 0;};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() = 0; virtual Body* CreateBody() = 0; virtual Fur* CreateFur() = 0; 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 phead; auto_ptr pbody; auto_ptr
pfur;};int _tmain(int argc, _TCHAR* argv[]){ auto_ptr
sheep(new SheepFactory()); Animal xiyangyang(*sheep); auto_ptr
wolf(new WolfFactory()); Animal huitailang(*wolf); return 0;}

运行结果如下:

转载于:https://www.cnblogs.com/mod109/p/6070432.html

你可能感兴趣的文章
How to make a simplest WCF service work on Win7 with VS2010
查看>>
js实现复选框全选和不选
查看>>
[ Java4Android ] Java的变量
查看>>
css:width height
查看>>
bzoj 4503: 两个串
查看>>
SQL中的共享锁分析及如何解锁
查看>>
Eclipse插件:mybatis generator的使用步骤
查看>>
/etc/profile不生效问题
查看>>
Scrapy教程,亲测能用
查看>>
HDU 5969 最大的位或【贪心/按位或/思维】
查看>>
用CDNs和Expires改善网站性能(译文)
查看>>
flask form表单验证
查看>>
DIV+CSS 图文混排的图片居中办法
查看>>
Java transient关键字使用小记
查看>>
ubuntu下nginx的启停等常用命令
查看>>
JavaSE 键盘事件类(KeyEvent)实现
查看>>
设计模式-缓存工厂模式代码构造
查看>>
PMF:为何硅谷大神把它念奉为创业公司“唯一重要的东西”
查看>>
CSS框模型
查看>>
SurfaceView 绘制分形图
查看>>