1. 程式人生 > >工廠模式:封裝物件的建立(一、在基類中定義一個靜態成員函式)

工廠模式:封裝物件的建立(一、在基類中定義一個靜態成員函式)

當我們發現需要新增新的型別到一個系統中時,最明智的首要步驟就是用多型機制為這些新型別建立一個共同的介面。

用這種方法可以將系統中多餘的程式碼與新新增的特定型別的程式碼分開。新型別的新增並不會攪亂已存在的程式碼...或者至少看上去如此。

起初它似乎只需要在繼承新類的地方修改程式碼,但這並非完全正確,仍須建立物件,在建立物件的地方必須指定要使用的準確的建構函式。

因此,如果建立物件的程式碼遍佈整個應用的程式,在增加新型別時將會遇到同樣的問題---------仍然必須找出程式碼中所有與新型別相關的地方。

這是由類的建立而不是型別的使用(型別的使用問題已經被多型機制解決了)而引起的。

解決該問題的方法就是強制用一個通用的工廠(factory)來建立物件,而不允許將建立物件的程式碼散佈整個系統。

如果程式中所有的需要建立物件的程式碼都轉到這個工廠(factory)執行,那麼在新增加物件時所要做的全部工作就是隻需要修改工廠。

這種設計就是工廠方法(Factory Method)模式的一種變體。

舉個例子:(實現工廠模式的一種方法就是在基類中定義一個靜態成員函式)

#include <stdexcept>
#include <cstddef>
#include <string>
#include <vector>
#include <algorithm>
#include <iostream>


using namespace std;


template<class Seq> void purge(Seq& c)  
{
    typename Seq::iterator i;
    for(i = c.begin(); i != c.end(); ++i) 
    {   
        delete *i; 
        *i = 0;
    }   
}


// Iterator version:
template<class InpIt> void purge(InpIt begin, InpIt end) 
{
    while(begin != end) 
    {   
        delete *begin;
        *begin = 0;
        ++begin;
    }   
}
class Shape 
{
public:
    virtual void draw() = 0;
    virtual void erase() = 0;
    virtual ~Shape() {}
    class BadShapeCreation : public logic_error 
    {   
    public:
    BadShapeCreation(string type): logic_error("Cannot create type " + type) {}
    };  


    static Shape* factory(const string& type)
    throw(BadShapeCreation);
};


class Circle : public Shape 
{
    Circle() {} // Private constructor
    friend class Shape;
public:
    void draw()
    {
        cout << "Circle::draw" << endl;
    }
    void erase()
    {
        cout << "Circle::erase" << endl;
    }


    ~Circle()
    {
        cout << "Circle::~Circle" << endl;
    }
};
class Square : public Shape
{
    Square() {}
    friend class Shape;
public:
    void draw()
    {
        cout << "Square::draw" << endl;
    }
    void erase()
    {
        cout << "Square::erase" << endl;
    }
    ~Square()
    {
        cout << "Square::~Square" << endl;
    }
};
Shape* Shape::factory(const string& type)
throw(Shape::BadShapeCreation) 
{
    if(type == "Circle") return new Circle;
    if(type == "Square") return new Square;
    throw BadShapeCreation(type);
}


char* sl[] = { "Circle", "Square", "Square","Circle", "Circle", "Circle", "Square"};




int main()
{


    vector<Shape*> shapes;
    try {
        for(size_t i = 0; i < sizeof sl / sizeof sl[0]; i++)
            shapes.push_back(Shape::factory(sl[i]));
    } catch(Shape::BadShapeCreation e)
    {
        cout << e.what() << endl;
        purge(shapes);
        return -1;
    }
    for(size_t i = 0; i < shapes.size(); i++)
    {
        shapes[i]->draw();
        shapes[i]->erase();
    }
    purge(shapes);
}

函式factory ()允許以一個引數來決定建立何種型別的Shape  在這裡,引數型別為string  也可以是任何資料集 。

在新增新的Shape型別時,函式factory()是當前系統中唯一需要修改的程式碼。

(物件的初始化資料大概也可以由系統外獲得。而不必像本例中來自硬編碼陣列。)

為了確保物件的建立只能發生在函式factory()中,

Shape的特定型別的建構函式被設為私有的,同時Shape被宣告為友元類,因此factory()能夠訪問這些建構函式。

(也可以只將Shape::factory() 宣告為友元函式,但是似乎宣告整個基類為友元類也沒什麼大礙。)

這樣的設計還有另外一個重要的含義——基類Shape現在必須瞭解每個派生類的細節——這是面向物件設計試圖避免的一個性質,

對於結構框架或者任何類庫來說都應該支援擴充,但是這樣一來,系統很快就會變得笨拙,因為一旦新型別被加入到這種層次結構中,

基礎類就必須更新,可以使用多型工廠(polymorphic    factory)來避免這種迴圈依賴。