全國最多中醫師線上諮詢網站-台灣中醫網
發文 回覆 瀏覽次數:1437
推到 Plurk!
推到 Facebook!

[推薦]Abstract Factory 模式在C++中的一種實現

 
axsoft
版主


發表:681
回覆:1056
積分:969
註冊:2002-03-13

發送簡訊給我
#1 引用回覆 回覆 發表時間:2002-08-14 16:21:00 IP:61.218.xxx.xxx 未訂閱

Abstract Factory 模式在C 中的一種實現

說明:Abstract Factory 是在編程實踐中最為常用的設計模式之一,本文介紹了在C 中的一種實現方式及其應用。 by: 方泓 資料來源:http://www.cpp3d.com/articles/show.asp?aid=102 介紹 Abstract Factory (抽象工廠),在GoF的書[1]中是這樣定義的:Provide an interface for creating families of related or dependent objects without specifying their concrete classes. (提供一個創建一系列相關或相互依賴對象的接口,而無需指定它們具體的類。) Abstract Factory適用于: * 一個系統要獨立于它的產品的創建、組合和表示時。 * 一個系統要由多個產品系列中的一個來配置時。 * 當你要強調一系列相關的產品對象的設計以便進行聯合使用時。 * 當你提供一個產品類庫,而只想顯示它們的接口而不是實現時。 由于Abstract Factory模式具有以上這些特色,因此它是一種在編程實踐中常用的模式,同時也產生了無數種實現Abstract Factory方法,各有不同的優缺點和適用範圍。這里我介紹目前我所用的一種的具體實現及其使用示例。 實現 Jim Hyslop 和 Herb Sutter 在 CUJ 上介紹過一種實現方法[2],它的原理是根據classid來動態創建類實例。但有時候這種解決方案在有些情況下也有一些不足之處,很多時候我們在創建時需要做一些複雜一點的處理才能知道對象的類型,用這種實現就無法方便的構造出所需要的類實例,顯然更為通用的情況是根據一個條件來進行判斷要創建哪個類實例。因此我在這增加了一種Abstract Factory的實現,它根據一個判斷函數來創建類實例,我採用了一個返回值為bool值的函數指針來做為判斷函數。 在這里我把這兩種不同的AbstractFactory實現分別稱為:TLookupFactory 和 TPredicateFactory,TLookupFactory實際就是[2]中的實現。下面我們來看看它們的具體實現,實際兩者的基本原理是一樣的,在內部維護一個registry來保存必要的創建信息。 在TLookupFactory中,registry是一個map,這個map的定義是:
        typedef std::map        CRegistry;
        CRegistry         m_registry;
PFN_BaseCreate就是創建函數的指針類型。正是通過將 TClassIDKey (在實際中TClassIDKey的類型最常用的應該是string類型,當然也可以是整數或別的其它類型) 和 PFN_BaseCreate 建立起一個map關系,所以TLookupFactory能夠根據不同的classid快速創建出其所對應的類型,具體創建代碼如下:
        boost::shared_ptr        Create(const TClassIDKey& className) const
        {
                boost::shared_ptr        ret(0);
                CRegistry::const_iterator        regEntry = m_registry.find(className);
                if (regEntry != m_registry.end())
                        ret = regEntry->second();
                return ret;
        }
可以看到,我們在此使用了boost中shared_ptr技術[3],因此我們可以方便的使用所創建出類實例的指針而不用擔心釋放問題。 而在TPredicateFactory,registry是一個vector,它的定義為:
        typedef std::vector        CRegistry;
        CRegistry         m_registry;    
可見vector里的元素是一對函數指針,PFN_IsMe就是那個判斷函數的指針,我們遍歷registry里的每一項,每次用相應的參數調用這個判斷函數,若返回值為真,則執行對應的創建函數來創建類實例。所以比起TLookupFactory來,TPredicateFactory的Create要略為複雜一些:
        boost::shared_ptr        Create(const TParam& tParam) const
        {
                boost::shared_ptr        ret(0);
                for (CRegistry::const_iterator it = m_registry.begin(); it != m_registry.end();    it)
                {
                        if (it->first(tParam))
                        {
                                ret = it->second();
                                break;
                        }
                }
                return ret;
        }
開始時registry里是空的,需要將創建函數的指針等內容注冊進去,由于使用的是STL中的map和vector,因此注冊特別簡單: 在TLookupFactory里是:
        void        RegCreateFunction(const TClassIDKey& clName, PFN_BaseCreate func)
        {
                m_registry[clName] = func;
        }    在TPredicateFactory中是:            void        RegCreateFunction(PFN_IsMe pfnIsMe, PFN_BaseCreate func)
        {
                m_registry.push_back(std::make_pair(pfnIsMe, func));
        }    為了方便需要通過Abstract Factory構造自己的子類注冊,在這里我在類TLookupFactory和TPredicateFactory中創建一個子類TRegister,它的實現是是以下這樣:    class        TPredicateFactory
{
...
public:
template
class        TRegister
{
public:
        TRegister(PFN_IsMe pfnIsMe)
        {
                TMyFactory::Instance()->RegCreateFunction(pfnIsMe,
                        TRegisterHelper::CreateInstance);
        }
};
};
在這里我們通過構造函數來簡化相應的構造工作。 為了使一部分代碼可重用,這里我還定義了一個基類 TAbstractFactory,TLookupFactory 和 TPredicateFactory 都從此類派生出來: Abstract Factory通常實現為一個Singleton,在此我也不例外,實際在前面的注冊代碼中已經可以看出這點了。 具體的實現細節見 Listing 1 。 使用 接著我們來看看如何使用這兩種Abstract Factory,假設我們有個虛基類定義如下:
class        CAbstractBase
{
public:
        virtual void        DoSomething(void) const = 0;
};    派生類為CBase和CDerived,各自重載了DoSomething。在CBase和CDerived中還各自定義了static成員函數IsMe如下:            bool        CBase::IsMe(const int& i)
        {
                return i == 0 ? true : false;
        }
        bool        CDerived::IsMe(const int& i)
        {
                return i == 1 ? true : false;
        }    下面是我們來看如何使用Factory來創建這兩種類實例:    typedef TPredicateFactory        CTestFactory;
int        main(void)
{
        boost::shared_ptr
                pNewBase = CTestFactory::Instance()->Create(0);
        boost::shared_ptr
                pNewDer = CTestFactory::Instance()->Create(1);
        pNewBase->DoSomething();
        pNewDer->DoSomething();
}    在代碼中也別忘了注冊一下,這是一件非常簡單的工作:    namespace
{
CTestFactory::TRegister        registerMe(CBase::IsMe);
CTestFactory::TRegister        registerMe_(CDerived::IsMe);
}
一個具體測試例子可見Listing 2 ,它的執行結果是:
Hi, I'm a Base class (1001) .
Hi, I'm a Derived
        My parent says:Hi, I'm a Base class (1001) .
--------------------------------
Hi, I'm a Base class (1002) .
Hi, I'm a Derived
        My parent says:Hi, I'm a Base class (1002) .
下面我們再看一個更實用一點的應用:在最近的flipcode的"Code of The Day"[4]上,Andreas Magnusson介紹了一種他應用Jim Hyslop和Herb Sutter的方法在Image裝載和保存上的應用,的確,處理裝載和保存各種Image格式是Abstract Factory極佳的應用實例。但他的方法是根據image的文件名后綴來創建相應的類實例,這樣並不科學,應該根據image的內容而不是文件名后綴來判斷。下面我介紹用本文實現的Abstract Factory如何做到這點。 假設我們有一個類CBitmap,它負責Load各種圖形格式,在這里我們可以應用Abstract Factory,我們在類里定義一個成員:boost::shared_ptr m_pImageLoader,由它來生成實際的Loader類實例,使用起來也非常方便,可以這樣定義為:
class        CBitmap
{
...
public:
        bool        Load(const TCHAR* pszName, EPixelFormat eDestPixelFormat);
...
protected:
        boost::shared_ptr        m_pImageLoader;
...
};
typedef TPredicateFactory        CImageLoaderFactory;
上面代碼中CImageLoaderBase是所有Image Loader類的基類。Load函數的一個可能的實現如下,注意其中m_pImageLoader是如何通過CImageLoaderFactory創建的:
bool        CBitmap::Load(const TCHAR* pszName, EPixelFormat eDestPixelFormat)
{
        m_pImageLoader = CImageLoaderFactory::Instance()->Create(std::tstring(pszName));
        if (m_pImageLoader.get() == NULL)
                return false;            return m_pImageLoader->MakeBmp(pszName, this, eDestPixelFormat);
}
接下來我們以Image格式中的一種Jpge來做為示例,來看看實現類中所需要的一些相關代碼:CJpegLoader定義在JpegLoader.h 中,是基類CImageLoaderBase中派生出來的,我們需要在其中加入一個判斷函數,可能是如下這樣:
class        CJpegLoader : public CImageLoaderBase
{
...
public:
        static bool        IsMe(const std::tstring& strName);
};
在相應的.cpp中需要注冊一下: namespace { CImageLoaderFactory::TRegister registerMe(CJpegLoader::IsMe); }; 再來看看判斷函數的一個可能實現:
bool        CJpegLoader::IsMe(const std::tstring& strName)
{
        CPackFile        packFile;
        if (packFile.Open(CPackFileManager::Instance(), strName.c_str()) == false)
                return false;            BYTE        pData[3];
        packFile.Read(pData, sizeof(pData));            // Check for JPEG/JFIF.
        if ((*pData == 0xFF) && (*(pData   1) == 0xD8) &&
                (*(pData   2) == 0xFF))
                return true;            return false;
}
這樣就可以了。如果我們需要新加一種image類型,並不需要修改和/或重新編譯CBitmap中的代碼,程序就能自動識別裝載這種新類型的Image,只要按規範實現並在這個CImageLoaderBase的派生類的.cpp文件中簡單注冊一下就行。 總結 從上面介紹的兩種Abstract Factory的使用示例中可以看出這種實現Abstract Factory是使用起來比較簡單,挺實用。但不足之處是仍然不夠通用,比如說有時候判斷函數可能需要兩個參數才能判斷,那還得再寫一個實現。一個可能的解決方法是使用loki中的typelists技術。 關于本文你有什麼想法或你有更好的解決方法,請和我交流。 注釋 [1] Gamma, Helm, Johnson, Vlissides. Design Patterns: Elements of Reusable Object-Oriented Software (Addison Wesley, 1995). (中譯本) 設計模式:可複用面向對象軟件的基礎 (機械工業出版社, 2000). [2] Jim Hyslop and Herb Sutter. "Conversations: A Factory, Template Style," C/C Users Journal C Experts Forum, June 2001. [3] . [4] Andreas Magnusson. "Image Factory", Flipcode "Code Of The Day". 附錄
    Listing 1    #ifndef _ABSTRACTFACTORY__H
#define _ABSTRACTFACTORY__H    typedef std::tstring        TFactoryDefaultIDKey;    template
class        TAbstractFactoryBase
{
public:
        typedef TManufacturedObj        TManufacturedObj;
        typedef boost::shared_ptr (*PFN_BaseCreate)(void);    public:
template
class        TRegisterHelper
{
public:
        static boost::shared_ptr        CreateInstance(void)
        {
                return boost::shared_ptr(new TDescendant);
        }
};    };    template
class        TLookupFactory
        : public TAbstractFactoryBase, private TSingleton
{
        typedef TLookupFactory        TMyFactory;
        M_DefineSingleton(TLookupFactory, TManufacturedObj::sc_nClassPhase);
private:
        TLookupFactory(void)
                :
                baseclass()
                ,m_registry()
        {
        }
        ~TLookupFactory()
        {
                m_registry.clear();
        }    public:
        void        RegCreateFunction(const TClassIDKey& clName, PFN_BaseCreate func)
        {
                m_registry[clName] = func;
        }            boost::shared_ptr        Create(const TClassIDKey& className) const
        {
                boost::shared_ptr        ret(0);
                CRegistry::const_iterator        regEntry = m_registry.find(className);
                if (regEntry != m_registry.end())
                {
                        ret = regEntry->second();
                }
                return ret;
        }    private:
        typedef std::map        CRegistry;
        CRegistry        m_registry;    public:
template
class        TRegister
{
public:
        TRegister(const TClassIDKey& id)
        {
//                M_CAssert((boost::is_base_and_derived::value));
                M_CAssertIsTypeof(TManufacturedObj, TDescendant);
                TMyFactory::Instance()->RegCreateFunction(id,
                        TRegisterHelper::CreateInstance);
        }
};    };    template
class        TPredicateFactory
        : public TAbstractFactoryBase, private TSingleton
{
        typedef TPredicateFactory        TMyFactory;
        M_DefineSingleton(TPredicateFactory, TManufacturedObj::sc_nClassPhase);
private:
        TPredicateFactory(void)
                :
                baseclass()
                ,m_registry()
        {
        }
        ~TPredicateFactory()
        {
                m_registry.clear();
        }    public:
        typedef bool        (*PFN_IsMe)(const TParam&);            void        RegCreateFunction(PFN_IsMe pfnIsMe, PFN_BaseCreate func)
        {
                m_registry.push_back(std::make_pair(pfnIsMe, func));
        }
#if 0
        template
        void        RegCreateFunction(meta::TType2Type, PFN_BaseCreate func)
        {
                RegCreateFunction(TDescendant::IsMe, func);
        }
#endif            boost::shared_ptr        Create(const TParam& tParam) const
        {
                boost::shared_ptr        ret(0);
                for (CRegistry::const_iterator it = m_registry.begin(); it != m_registry.end();    it)
                {
                        if (it->first(tParam))
                        {
                                ret = it->second();
                                break;
                        }
                }
                return ret;
        }    private:
        typedef std::vector        CRegistry;
        CRegistry        m_registry;    public:
template
class        TRegister
{
public:
        TRegister(void)
        {
//                M_CAssert((boost::is_base_and_derived::value));
                M_CAssertIsTypeof(TManufacturedObj, TDescendant);
                TMyFactory::Instance()->RegCreateFunction(TDescendant::IsMe,
                        TRegisterHelper::CreateInstance);
        }
        TRegister(PFN_IsMe pfnIsMe)
        {
//                M_CAssert((boost::is_base_and_derived::value));
                M_CAssertIsTypeof(TManufacturedObj, TDescendant);
                TMyFactory::Instance()->RegCreateFunction(pfnIsMe,
                        TRegisterHelper::CreateInstance);
        }
};    };    #endif        // !_ABSTRACTFACTORY__H    Listing 2    #include "AbstractFactory.h"
#include     template
class        CAbstractBase
{
public:
        BOOST_STATIC_CONSTANT(int, sc_nClassPhase = t_nClassPhase);    public:
        virtual void        DoSomething(void) const = 0;
};    template
class        CBase : public CAbstractBase
{
public:
        virtual void        DoSomething(void) const
        {
                std::tcout << _T("Hi, I'm a Base class (") << t_nClassPhase << _T(") .") << std::endl;
        }    public:
        static bool        IsMe(const int& i)
        {
                return i == 0 ? true : false;
        }
};    typedef TLookupFactory, TFactoryDefaultIDKey>        CTestFactory1;
typedef TPredicateFactory, int>        CTestFactory2;
M_RegisterTypeOfSingletonPhase(CTestFactory1)
M_RegisterTypeOfSingletonPhase(CTestFactory2)    int        _tmain(void)
{
        boost::shared_ptr >
                pNewBase = CTestFactory1::Instance()->Create(_T("Base"));
        boost::shared_ptr >
                pNewDer = CTestFactory1::Instance()->Create(_T("Derived"));
        pNewBase->DoSomething();
        pNewDer->DoSomething();            std::tcout << _T("--------------------------------") << std::endl;            boost::shared_ptr >
                pNewBase2 = CTestFactory2::Instance()->Create(0);
        boost::shared_ptr >
                pNewDer2 = CTestFactory2::Instance()->Create(1);
        pNewBase2->DoSomething();
        pNewDer2->DoSomething();            return 0;
}    namespace
{
CTestFactory1::TRegister >
        registerMe1(TFactoryDefaultIDKey(_T("Base")));
CTestFactory2::TRegister >
        registerMe1_;
}    template
class        CDerived : public CBase
{
        typedef CBase        baseclass;
public:
        virtual void        DoSomething(void) const
        {
                std::tcout << _T("Hi, I'm a Derived") << std::endl << _T("\tMy parent says:");
                baseclass::DoSomething();
        }    public:
        static bool        IsMe(const int& i)
        {
                return i == 1 ? true : false;
        }
};    namespace
{
class        CNotDerived{};
CTestFactory1::TRegister/*CNotDerived*/ >
        registerMe2(TFactoryDefaultIDKey(_T("Derived")));
CTestFactory2::TRegister >
        registerMe2_(CDerived<1002>::IsMe);
}
時間就是金錢---[ 發問前請先找找舊文章]
系統時間:2024-05-04 16:32:23
聯絡我們 | Delphi K.Top討論版
本站聲明
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。
2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。
3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇!