【转】设计模式学习笔记(1)--抽象工厂

设计模式学习笔记(1)-抽象工厂

 

抽象工厂模式是GoF《设计模式》讲解的第一个模式,属于创建型模式。
说到创建型模式,就是为了创建类而衍生的一系列设计模式。在实际编程中,往往简单的new创建对象并不能满足我们的需要,一般来说创建对象遵循的GRASP(职责分配模式)有:

当一下条件之一为真是,B类负责创建A类。
B由A聚合或组合、B记录A、B直接使用A、B具有A的初始化数据

我们在开发程序中常面临一个状况:我们希望程序在不同的模式下有着不同的表现。这就是面向对象中所说的多态性,而实现多态性的途径就是面向接口而不是面向实现编程。接口是固定的,而实现是可以动态改变的。这便降低了模块之间的耦合度,使得程序易于维护。
今天学习的抽象工厂模式就是为了这个目标而建立的。
在抽象工厂模式中,我们将信息交给工厂,有工厂负责创建实现类,考虑到多态性,我们的工厂也是抽象的。举个例子:
如果我们要做一个游戏场景,这个场景有树(Tree)和天空(Sky)对象,Tree对象有color方法可以获得它的颜色,Sky有drop方法可以实现降水。即:

1
2
3
4
5
6
7
8
9
10
class Tree
{
public:
    virtual void color() = 0;
};
class Sky
{
public:
    virtual void drop() = 0;
};

考虑到面向接口我们把这两个类定义为抽象类。然后写两个实现类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class TreeImpl : public Tree
{
public:
    void color()
    {
        cout << "The tree is green!" << endl;
    }
};
class SkyImpl : public Sky
{
public:
   void drop()
   {
       cout << "It rains" << endl; 
    }
};

好了,这样我们只要在主方法中调用

1
2
3
Tree* tree = new TreeImpl(); Sky* sky = new SkyImpl();
Tree->color();
Sky->drop();

就可以实现这样一个场景了:绿色的树,天空下着雨。
可是这样一来场景就固化了,如果要做一个冬天的场景呢?冬天可不会下雨,树也不是绿色的。也许这样做是可以的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class WinterTree : public Tree
{
public:
    void color()
    {
        cout << "The tree is black!" << endl;
    }
};
 
class SummerTree : public Tree
{
public:
    void color()
    {
        cout << "The tree is green!" << endl;
    }
};
class WinterSky : public Sky
{
public:
    void drop()
    {
        cout << "It snows" << endl;
    }
};
 
class SummerSky : public Sky
{
public:
    void drop()
    {
        cout << "It rains" << endl; 
    }
};

我们为每个场景各写了一个实现类,冬天有冬天的树,夏天有夏天的树。 在主方法中我们可以这样写: 冬天:

1
2
Tree* tree = new WinterTree ();
Sky* sky = new WinterSky();

夏天:

1
2
Tree* tree = new SummerTree ();
Sky* sky = new SummerSky();

看样子挺好,但是一旦要修改场景我们必须同时修改两处,如果该场景有成千上万个元素呢?比如说草、池塘?我们很自然地想到要有某个东西像下面这样就好了:

1
2
Tree* tree = SomeThing.createTree();
Sky* sky = SomeThing.createSky();

这个SomeThing就是工厂。我们可以通过更改它实现整个场景的更改,这样便避免了在冬天出现天空下雪,树确实绿色的怪事。 这里有两个场景,所以要有两个工厂,考虑到程序应该依赖于抽象,还要有一个抽象工厂作为所有工厂的父类。

1
2
3
4
5
class Factory
{
    publicvirtual Tree* createTree() = 0;
    virtual Sky* createSky() = 0;
};

这个工厂什么也不做,必须依靠子类来工作。这里是两个子类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class WinterFactory : public Factory
{
public:
    Tree* createTree()
    {
        return new WinterTree();
    }
    Sky* createSky()
    {
        return new WinterSky();
    }
};
class SummerFactory : public Factory
{
public:
    Tree* createTree()
    {
        return new SummerTree();
    }
    Sky* createSky()
    {
        return new SummerSky();
    }
};

WinterFactory负责创建冬天场景的元素,SummerFactory负责创建夏天场景的元素。这样一来,主方法中只需这样写:

1
2
3
4
5
Factory* factory = new SummerFactory();
Tree* tree = factory->createTree();
Sky* sky = factory->createSky();
tree->color();
sky->drop();

如果我们要修改成冬天的场景只需将第一行修改为:

1
Factory* factory = new WinterFactory();

即可。
全部代码只有这些,实现了一个最简单的抽象工厂。类图如下:
抽象工厂类图
我们这里类与类之间的连接全部通过抽象类(接口)来完成,减少了系统的耦合度。
来看一下GoF所说的抽象工厂的适用性:

  • 一个系统要独立与它的产品的创建、组合和表示时。
  • 一个系统要由多个产品系列中的一个来配置时。
  • 当你要强调一系列相关的产品对象的设计以便进行联合使用时。
  • 当你提供一个产品类库,而只想显示它们的接口而不是实现时。

在我们的这个例子中,冬天和夏天是两个系列,而树和天空是两个产品。一个系列中的产品表现必须一致,因此采用抽象工厂模式是十分恰当的。
关系图:
关系
这样以来就十分明朗了。

 

转自:http://lecoding.com/articles/51.html

posted on 2013-03-05 13:11  TheKingOfKingFish  阅读(128)  评论(0)    收藏  举报

导航