1. 程式人生 > >面向對象設計——抽象工廠(Abstract Factory)模式

面向對象設計——抽象工廠(Abstract Factory)模式

protected wiki tsp 客戶端 direct eat cot 優缺點 https

定義

  提供一個創建一系列相關或者相互依賴對象的接口,而無需指定它們具體的類。抽象工廠允許客戶使用抽象的接口來創建一組相關的產品,而不需要知道或關心實際產出的具體產品是什麽。這樣一來,客戶就能從具體的產品中被解耦。

適用場景

  在以下情況可以使用Abstract Factory模式

  • 一個系統要獨立於它的產品的創建、組合和表示時
  • 一個系統要由多個產品系列中的一個來配置時
  • 當你要強調一系列相關的產品對象的設計以便進行聯合使用時
  • 當你提供一個產品類庫,而只想顯示它們的接口而不是實現時

UML圖

技術分享

                  抽象工廠模式UML圖a)

  • AbstractFactory

  ——聲明一個創建抽象產品對象的操作接口。

  • ConcreteFactory

  ——實現創建具體產品對象的操作。

  • AbstractProduct

  ——為一類產品對象聲明一個接口。

  • ConcreteProduct

  ——定義一個將被相應的具體工廠創建的產品對象。

  ——實現AbstractProduct接口。

  在這裏指的是ProductA1,ProductA2,ProductB1,ProductB2。

  • Client

  ——僅適用由AbstractFactory和AbstractProduct類聲明的接口。

  一般而言,有多少個產品等級結構,就會在工廠角色中發現多少個工廠方法。每一個產品等級結構中有多少個具體的產品,就有多少個產品族,也就會在工廠等級結構中發現多少個具體工廠。

代碼示例

  《設計模式:可復用面向對象軟件的基礎》中一書中,給出了一個應用工廠方法設計迷宮的例子,這裏修改為應用抽象工廠模式來實現。

  首先,給出了枚舉類型Direction表明東南西北四個方向,定義抽象產品AbstractProduct接口,包含一個純虛函數Enter,用於在迷宮間移動位置。

enum Direction {North, South, East, West};

//AbstractProduct
class MapSite{
public:
    virtual void Enter() = 0;
};

  接下來,基於抽象產品接口MapSite實現ConcreteProduct

具體產品類:

  定義迷宮房間Room類(ProductA);

//ProductA
class Room : public MapSite{
public:
    Room(int roomNo);//初始化房間號

    MapSite *GetSide(Direction) const;//獲取當前房間四周具體產品ConcreteProduct類MapSite
    void SetSide(Direction, MapSite*);//設置當前房間四周具體產品ConcreteProduct類MapSite
   virtual void Enter(); 
private:
   MapSite
* _side[4];
   int _roomNumber;
};

  定義迷宮的墻Wall類(ProductB);

//ProductB
class Wall : public MapSite{
public:
    Wall();

    virtual void Enter();
};

   定義迷宮的門Door類(ProductC);

//ProductC
class Door : public MapSite{
public:
    Door(Room * = 0, Room * = 0);//初始化門兩邊的房間

    virtual void Enter();
    Room* OtherSideFrom(Room *);
private:
    Room * _room1;
    Room * _room2;
    bool _isOpen;
};

  定義迷宮Maze類(ProductD);

//ProductD
class Maze{
public:
    Maze();

    void AddRoom(Room *);
    Room* RoomNo(int) const;
private:
    //...
};

  在完成抽象產品接口和具體產品類定義後,接下來定義抽象工廠接口AbstractFactory類MazeFactory,對外提供接口,用於生成上述定義的具體產品類ProductA——Room,ProductB——Wall,ProductC——Door,ProductD——Maze。

//AbstractFactory
class MazeFactory{
public:
    MazeFactory();

    virtual Maze* MakeMaze() const
    {return new Maze;}
    virtual Wall* MakeWall() const 
    {return new Wall;}
    virtual Room* MakeRoom(int n) const
    {return new Room(n);}
    virtual Door* MakeDoor(Room* r1, Room* r2) const
    {return new Door(r1,r2);}
};

  在定義好抽象工廠接口、抽象產品接口、具體產品後,就可以由Client來動態創建和處理各個對象的關系,這裏創建一個包含兩個房間的簡單迷宮。

//Client
Maze * MazeGame::CreateMaze(MazeFactory &factory){ Maze * aMaze = factory.MakeMaze(); Room *r1 = factory.MakeRoom(1); Room *r2 = factory.MakeRoom(2); Door *aDoor = factory.MakeDoor(r1,r2); aMaze->AddRoom(r1); aMaze->AddRoom(r2); r1->SetSide(North, factory.MakeWall()); r1->SetSide(East, aDoor); r1->SetSide(South,factory.MakeWall()); r1->SetSide(West, factory.MakeWall()); r2->SetSide(North, factory.MakeWall()); r2->SetSide(East, factory.MakeWall()); r2->SetSide(South, factory.MakeWall()); r2->SetSide(West, aDoor); return aMaze; }

  如果需要創建帶各種特效或者屬性的Room、Door、Wall ,只需要繼承具體產品類ProductA、ProductB、ProductC、ProductD,以及抽象工廠AbstractFactory接口MazeFactory。

  如果要生成魔法迷宮,只需要繼承MazeFactory生成ConcreteFactory類EnchantedMazeFacotry,

//ConcreteFactory
class EnchantedMazeFactory:public MazeFactory{
public:
    EnchantedMazeFacotry();

    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;
};

  調用CreateMaze就可以生成帶魔法房間和符咒門的迷宮了。。。

//Client
MazeFactory
*enchantedMazeFac = new EnchantedMazeFactory ;
Maze
*enchantedMaze = CreateMaze(enchantedMazeFac);

  同理還可以,由爆炸工廠生成帶爆炸門和爆炸房間的迷宮。。。

Wall * BombedMazeFactory::MakeWall() const {
    return new BombedWall;
}
Room* BombedMazeFactory::MakeRoom(int n) const{
    return new RoomWithABomb(n);
}

  如果嫌上面例子麻煩, 下面這個例子(截圖自wikipedia官網),簡單明了的闡釋了抽象工廠模式:

技術分享

優缺點

優點:

1、抽象工廠模式隔離了具體類的生產,使得客戶並不需要知道什麽被創建。

2、當一個產品族中的多個對象被設計成一起工作時,它能保證客戶端始終只使用同一個產品族中的對象。

3、增加新的具體工廠和產品族很方便,無須修改已有系統,符合“開閉原則”。

缺點:

增加新的產品等級結構很復雜,需要修改抽象工廠和所有的具體工廠類,對“開閉原則”的支持呈現傾斜性。

參考資料:

a) https://en.wikipedia.org/wiki/Abstract_factory_pattern

b) 《設計模式:可復用面向對象軟件的基礎》

c) http://www.runoob.com/design-pattern/abstract-factory-pattern.html

d) https://zh.wikipedia.org/wiki/%E6%8A%BD%E8%B1%A1%E5%B7%A5%E5%8E%82

面向對象設計——抽象工廠(Abstract Factory)模式