工厂方法(Factory Method) -- 对象创建型模式

意图

定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method使一个类的实例化延迟到了其子类。

适用性

  • 当一个类不知道它所必须创建的对象的类的时候。
  • 当一个类希望由它的子类来指定它所创建的对象的时候。
  • 当类创建对象的职责委托给多个帮助子类中的某一个,并且希望这个帮助子类的信息局部化,即需要降低耦合。

结构

Factory Method

  • Product
    定义工厂方法所创建对象的接口。
  • ConcreteProduct
    实现Product接口。
  • Creator
    声明工厂方法,该方法返回一个Product类型的对象。Creator也可以定义一个工厂方法的缺省实现,它返回一个缺省的ConcreteProduct对象。
  • ConcreteCreator
    重定义工厂方法以返回一个ConcreteProduct实例。

协作

Creator依赖于它的子类来定义工厂方法,所以需要它返回一个适当的ConcreteProduct实例。

效果

工厂方法不再将特定应用有关的类绑定到你的代码中,代码仅处理Product接口,因此它可以与用户定义的任何ConcreteProduct类一起使用,提高了可扩展性,降低了耦合度。
工厂方法一个潜在缺点是客户为了创建一个特定的ConcreteProduct对象,就不得不创建Creator的子类。

实现

需要考虑以下几个方面:
1)有两种情况,一是Creator类是抽象类不提供它所声明的工厂方法的实现;二是Creator是一个具体的类为工厂方法提供了一个缺省的实现。
第一种情况必须子类来定义实现,因为没有缺省实现,它避免了不得不实例化不可预见类的问题。第二种情况,使子类的设计者能够在必要时才需要改变父类所实例化对象的类。
2)参数化工厂方法,工厂方法也可以使用一个标识符作为参数来决定要创建的对象种类。

class Creator {
public:
    virtual Product* Creator(ProductId);
};

Product* Creator::Create (ProductId id) {
    if (id == MINE) return new MyProduct;
    if (id == YOUS) return new YourProduct;
    // repeat for remaining products...
    return 0;
}

参数化得工厂方法使你可以简单而有选择性的扩展或改变一个Creator生产的产品。可以为新产品引入新的标识符,或可以将已有的标识符与不同的产品关联。
3)不要在构造器中调用虚函数。C++中工厂方法都是虚函数并且常常是纯虚函数,一定要注意在Creator的构造器中不要调用工厂方法,因为此时ConcreteCreator中的该工厂方法还不可用。
4)使用模板以避免创建子类。正如上面提及的,工厂方法一个潜在的问题是它们仅为了创建适当的Product而必须创建Creator子类,一个解决方法就是提供一个Creator的模板子类,它使用Product类作为模板参数:

class Creator {
public:
    virtual Product* CreateProduct() = 0;
};

template <class TheProduct>
class StandardCreator: public Creator {
public:
    virtual Product* CreateProduct();
};

template <class TheProduct>
Product* StandardCreator<TheProduct>::CreatProduct() {
 return new TheProduct;
}

//使用这个模板,客户仅需提供产品类,而不需要创建Creator子类
class MyProduct : public Product {
public:
    MyProduct();
    // ...
};

StandardCreator<MyProduct> myCreator;

5) 命名约定。使用命名约定是一个好习惯,它可以清楚的说明你正在使用工厂方法。例如可以总是定义工厂方法为:Class* DoMakeClass(),此次Class为Product类。

示例

// MazeGame.hpp
// 有默认实现的工厂方法基类
#pragma once

#include "Maze.hpp"
class MazeGame {
public:
    Maze * CreateMaze();

    // factory methods:
    virtual Maze* MakeMaze() const
    {
        return new Maze;
    }

    virtual Room* MakeRoom(int n) const
    {
        return new Room(n);
    }

    virtual Wall* MakeWall() const
    {
        return new Wall;
    }

    virtual Door* MakeDoor(Room* r1, Room* r2) const
    {
        return new Door(r1, r2);
    }
};

Maze* MazeGame::CreateMaze()
{
    Maze* pMaze = MakeMaze();

    Room* r1 = MakeRoom(1);
    Room* r2 = MakeRoom(2);

    Door* pDoor = MakeDoor(r1, r2);

    pMaze->AddRoom(r1);
    pMaze->AddRoom(r2);

    r1->SetSide(North, MakeWall());
    r1->SetSide(East, pDoor);
    r1->SetSide(South, MakeWall());
    r1->SetSide(West, MakeWall());

    r2->SetSide(North, MakeWall());
    r2->SetSide(East, MakeWall());
    r2->SetSide(South, MakeWall());
    r2->SetSide(West, pDoor);

    return pMaze;
}

不同的游戏可以创建MazeGame的子类并特化一些迷宫组件,即MazeGame子类可以重定义一些或所有的工厂方法以指定产品中的变化。例如,一个BombedMazeGame可以重定义产品Room和Wall以返回爆炸后的变体:

class BombedMazeGame : public MazeGame {
public:
    BombedMazeGame() {}

    virtual Wall* MakeWall() const
    {
        return new BombedWall;
    }

    virtual Room* MakeRoom(int n) const
    {
        return new RoomWithABomb(n);
    }
};

一个EnchantedMazeGame变体可以这样定义:

class EnchantedMazeGame : public MazeGame {
public:
    EnchantedMazeGame() {}

    virtual Room* MakeRoom(int n) const
    {
        return new EnchantedRoom(n, CastSpell());
    }
    virtual Door* MakeDoor(Room* r1, Room* r2) const
    {
        return new DoorNeedingSpell(r1, r2);
    }
protected:
    Spell * CastSpell() const
    {
        std::cout << "Cast Spell" << std::endl;
    }
};

相关模式

Abstract Factory经常使用工厂方法来实现。
工厂方法通常在Template Methods中被调用。
Prototypes不需要创建Creator的子类,但它们通常要求一个针对Product类的Initialize操作。即Creator使用Initialize来初始化对象,而Factory Method不需要。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值