青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

eryar

PipeCAD - Plant Piping Design Software.
PlantAssistant - Translate AVEVA RVM/SP3D VUE to glTF, STEP, etc.
posts - 606, comments - 590, trackbacks - 0, articles - 0

工廠方法Factory Methods

Posted on 2013-03-22 21:20 eryar 閱讀(5640) 評論(4)  編輯 收藏 引用

Factory Methods

工廠方法

eryar@163.com

摘要Abstract:本文主要是對《API Design for C++》中Factory Methods章節的翻譯,若有不當之處,歡迎指正。

關鍵字Key Words:C++、Factory Pattern、

一、概述 Overview

工廠方法是創建型模式,允許在不指定需要創建對象類型的情況下創建出對象。本質上來說,工廠方法就是一個通用的構造函數。C++中的構造函數有以下幾種限制:

l 無返回值(No return result)。在構造函數中不能返回一個值。這就意味著:例如當構造失敗時不能返回一個NULL作為初始化失敗的信號。

l 命名有約束(Constrained naming)。構造函數還是很好識別的,因為它的命名必須與類名一樣。

l 靜態綁定創建的(Statically bound creation)。當創建一個對象時,必須指定一個在編譯時就能確定的類名。如:Foo *f = new Foo(),Foo就是編譯器必須知道的類名。C++的構造函數沒有運行時的動態綁定功能(dynamic binding at run time)。

l 無虛構造函數(No virtual constructors)。在C++中不能聲明虛的構造函數,必須指定在編譯時能確定的類型。編譯器據此為指定的類型分配內存,然后調用基類的默認構造函數,再調用指定類的構造函數。這就是不能定義構造函數為虛函數的原因。

相反地,工廠方法(factory methods)突破了以上所有的限制。工廠方法的基本功能就是一個可以返回一個類的實例的簡單函數。但是,它通常與繼承組合使用,派生的類可以重載工廠方法以返回派生類的實例。使用抽象基類(Abstract Base Classes)來實現工廠很常見,也很有用。

二、抽象基類 Abstract Base Classes

抽象基類就是包含一個或多個純虛函數(pure virtual methods)的類,這樣的類不是具體類且不能用new來實例化。相反地,它是作為其它派生類的基類,由派生類來具體實現那些純虛函數。例如:

#ifndef RENDERER_H
#define RENDERER_H

#include 
<string>

///
/// An abstract interface for a 3D renderer.
///
class IRenderer
{
public:
    
virtual ~IRenderer() {}
    
virtual bool LoadScene(const std::string &filename) = 0;
    
virtual void SetViewportSize(int w, int h) = 0;
    
virtual void SetCameraPos(double x, double y, double z) = 0;
    
virtual void SetLookAt(double x, double y, double z) = 0;
    
virtual void Render() = 0;
};

#endif

上述代碼定義了一個抽象基類,描述了一個相當簡單的3D圖形渲染器(renderer)。函數的后綴“=0”聲明這個函數是純虛函數,表示這個函數必須由其派生類來具體實現。

抽象基類是描述了多個類共有的行為的抽象單元,它約定了所有具體派生類必須遵守的合同。在Java中,抽象基類也叫接口(interface),只是Java的接口只能是公用的方法(public method),靜態變量,并且不能定義構造函數。將類名IRenderer帶上“I”就是為了表明這個類是接口類(interface class)。

當然,抽象基類中并不是所有的方法都必須是純虛函數,也可以實現一些函數。

當任意一個類有一個或多個虛函數時,通常會把抽象基類的析構函數聲明為虛函數。如下代碼說明了這樣做的重要性:

class IRenderer
{
    
// no virtual destructor declared
    virtual void Render() = 0;
};

class RayTracer : public IRenderer
{
    RayTracer();
    
~RayTracer();
    
void Render(); // provide implementation for ABC method
};

int main(intchar **)
{
    IRenderer 
*= new RayTracer();
    
// delete calls IRenderer::~IRenderer, not RayTracer::~RayTracer
    delete r;
}

 

三、簡單工廠模式 Simple Factory Example

在復習了抽象基類后,讓我們在簡單工廠方法中使用它。繼續以renderer.h為例,聲明創建工廠,創建的對象類型為IRenderer,代碼如下所示:

#ifndef RENDERERFACTORY_H
#define RENDERERFACTORY_H

#include 
"renderer.h"
#include 
<string>

///
/// A factory object that creates instances of different
/// 3D renderers.
///
class RendererFactory
{
public:
    
/// Create a new instance of a named 3D renderer.
    
/// type can be one of "opengl", "directx", or "mesa"
    IRenderer *CreateRenderer(const std::string &type);
};

#endif

這里只聲明了一個工廠方法:它只是一個普通的函數,返回值是對象的實例。注意到這個方法不能返回一個指定類型的IRender實例,因為抽象基類是不能被實例化的。但是它可以返回派生類的實例。當然,你可以使用字符串作為參數來指定需要創建對象的類型。

假設已經實現了派生自IRender的三個具體類:IRenderer::OpenGLRenderer,DirectXRenderer、MesaRenderer。再假設你不想讓使用API的用戶知道可以創建哪些類型:他們必須完全隱藏在API后面?;谶@些條件,可以實現工廠方法的程序如下:

// rendererfactory.cpp
#include "rendererfactory.h"
#include 
"openglrenderer.h"
#include 
"directxrenderer.h"
#include 
"mesarenderer.h"

IRenderer 
*RendererFactory::CreateRenderer(const std::string &type)
{
    
if (type == "opengl")
        
return new OpenGLRenderer;

    
if (type == "directx")
        
return new DirectXRenderer;

    
if (type == "mesa")
        
return new MesaRenderer;

    
return NULL;
}

這個工廠方法可以返回IRenderer的三個派生類之一的實例,取決于傳入的參數字符串。這就可以讓用戶決定在運行時而不是在編譯時創建哪個派生類,這與普通的構造函數要求一致。這樣做是有很多好處的,因為它可以根據用戶輸入或根據運行時讀入的配置文件內容來創建不同的對象。

另外,注意到實現具體派生類的頭文件只在rendererfactory.cpp中被包含。它們不出現在rendererfactory.h這個公開的頭文件中。實際上,這些頭文件是私有的頭文件,且不需要與API一起發布的。這樣用戶就看不到不同的渲染器的私有細節,也看不到具體可以創建哪些不同的渲染器。用戶只需要通過字符串變量來指定種要創建的渲染器(若你愿意,也可用一個枚舉來區分類型)。

此例演示了一個完全可接受的工廠方法。但是,其潛在的缺點就是包含了對可用的各派生類的硬編碼。若系統需要添加一個新的渲染器,你必須再編輯rendererfactory.cpp。這并不會讓人很煩,重要的是不會影響你提供的公用的API。但是,他的確不能在運行時添加支持的新的派生類。再專業點,這意味著你的用戶不能向系統中添加新的渲染器。通過擴展的對象工廠來解決這些問題。

四、擴展工廠模式 Extensible Factory Example

為了讓工派生類從工廠方法中解耦,且允許在運行時添加新的派生類,可以去維護包含類型及與類型創建關聯的函數的映射(map)來更新一下工廠類。可以通過添加幾個新的函數用來注冊與注銷新的派生類。在運行時能注冊新的類允許這種類型的工廠方法模式可用于創建可擴展的接口。

還有個需要注意的事是工廠對象必須保存狀態,即最好只有一個工廠對象。這也是工廠對象通常是單件的(singletons)。為了程序的簡單明了,這里使用靜態變量為例。將所有要點都考慮進來,新的工廠對象代碼如下所示:

#ifndef RENDERERFACTORY_H
#define RENDERERFACTORY_H

#include 
"renderer.h"
#include 
<string>
#include 
<map>
///
/// A factory object that creates instances of different
/// 3D renderers. New renderers can be dynamically added
/// and removed from the factory object.
///
class RendererFactory
{
public:
    
/// The type for the callback that creates an IRenderer instance
    typedef IRenderer *(*CreateCallback)();

    
/// Add a new 3D renderer to the system
    static void RegisterRenderer(const std::string &type,
                                 CreateCallback cb);
    
/// Remove an existing 3D renderer from the system
    static void UnregisterRenderer(const std::string &type);

    
/// Create an instance of a named 3D renderer
    static IRenderer *CreateRenderer(const std::string &type);

private:
    typedef std::map
<std::string, CreateCallback> CallbackMap;
    
static CallbackMap mRenderers;
};

#endif

為了程序的完整性,將其.cpp文件中的代碼示例如下:

#include "rendererfactory.h"
#include 
<iostream>

// instantiate the static variable in RendererFactory
RendererFactory::CallbackMap RendererFactory::mRenderers;

void RendererFactory::RegisterRenderer(const std::string &type,
                                       CreateCallback cb)
{
    mRenderers[type] 
= cb;
}

void RendererFactory::UnregisterRenderer(const std::string &type)
{
    mRenderers.erase(type);
}

IRenderer 
*RendererFactory::CreateRenderer(const std::string &type)
{
    CallbackMap::iterator it 
= mRenderers.find(type);
    
if (it != mRenderers.end())
    {
        
// call the creation callback to construct this derived type
        return (it->second)();
    }

    
return NULL;
}

使用工廠對象創建派生類的方法如下所示:

#include "rendererfactory.h"
#include 
<iostream>

using std::cout;
using std::endl;

/// An OpenGL-based 3D renderer
class OpenGLRenderer : public IRenderer
{
public:
    
~OpenGLRenderer() {}
    
bool LoadScene(const std::string &filename) { return true; }
    
void SetViewportSize(int w, int h) {}
    
void SetCameraPos(double x, double y, double z) {}
    
void SetLookAt(double x, double y, double z) {}
    
void Render() { cout << "OpenGL Render" << endl; }
    
static IRenderer *Create() { return new OpenGLRenderer; }
};

/// A DirectX-based 3D renderer
class DirectXRenderer : public IRenderer
{
public:
    
bool LoadScene(const std::string &filename) { return true; }
    
void SetViewportSize(int w, int h) {}
    
void SetCameraPos(double x, double y, double z) {}
    
void SetLookAt(double x, double y, double z) {}
    
void Render() { cout << "DirectX Render" << endl; }
    
static IRenderer *Create() { return new DirectXRenderer; }
};

/// A Mesa-based software 3D renderer
class MesaRenderer : public IRenderer
{
public:
    
bool LoadScene(const std::string &filename) { return true; }
    
void SetViewportSize(int w, int h) {}
    
void SetCameraPos(double x, double y, double z) {}
    
void SetLookAt(double x, double y, double z) {}
    
void Render() { cout << "Mesa Render" << endl; }
    
static IRenderer *Create() { return new MesaRenderer; }
};


int main(intchar **)
{
    
// register the various 3D renderers with the factory object
    RendererFactory::RegisterRenderer("opengl", OpenGLRenderer::Create);
    RendererFactory::RegisterRenderer(
"directx", DirectXRenderer::Create);
    RendererFactory::RegisterRenderer(
"mesa", MesaRenderer::Create);

    
// create an OpenGL renderer
    IRenderer *ogl = RendererFactory::CreateRenderer("opengl");
    ogl
->Render();
    delete ogl;

    
// create a Mesa software renderer
    IRenderer *mesa = RendererFactory::CreateRenderer("mesa");
    mesa
->Render();
    delete mesa;

    
// unregister the Mesa renderer
    RendererFactory::UnregisterRenderer("mesa");
    mesa 
= RendererFactory::CreateRenderer("mesa");
    
if (! mesa)
    {
        cout 
<< "Mesa renderer unregistered" << endl;
    }

    
return 0;
}

你的API的用戶可以在系統中注冊與注銷一個新的渲染器。編譯器將會確保用戶定義的新的渲染器必須實現抽象基類IRenderer的所有抽象接口,即新的渲染器類必須實現抽象基類IRenderer所有的純虛函數。如下代碼演示了用戶如何自定義新的渲染器,在工廠對象中注冊,并叫工廠對象為之創建一個實例:

這里需要注意的一點是我向類UserRenderer中添加了一個Create()函數,這是因為工廠對象的注冊方法需要返回一個對象的回調函數。這個回調函數不一定必須是抽象基類IRenderer的一部分,它可以是一個自由的函數。但是向抽象基類IRenderer中添加這個函數是一個好習慣,這樣就確保了所有相關功能的一致性。實際上,為了強調這種約定,可以將Create作為抽象基類IRenderer的一個純虛函數。

五、結論 Conclusion

Finally, I note that in the extensible factory example given here, a renderer callback has to be

visible to the RegisterRenderer() function at run time. However, this doesn’t mean that you

have to expose the built-in renderers of your API. These can still be hidden either by registering

them within your API initialization routine or by using a hybrid of the simple factory and the extensible

factory, whereby the factory method first checks the type string against a few built-in names.

If none of those match, it then checks for any names that have been registered by the user. This hybrid

approach has the potentially desirable behavior that users cannot override your built-in classes.

 

PDF Version: Factory Method

Feedback

# re: 工廠方法Factory Methods[未登錄]  回復  更多評論   

2014-08-15 14:11 by lcalqf
寫的非常好

# re: 工廠方法Factory Methods  回復  更多評論   

2014-08-15 14:23 by eryar
不是我寫的,
是對《API Design for C++》中Factory Methods章節的翻譯。
@lcalqf

# re: 工廠方法Factory Methods  回復  更多評論   

2015-01-20 23:55 by douzi
實際上,為了強調這種約定,可以將Create作為抽象基類IRenderer的一個純虛函數。
請問,這一句的方法,如何實現?
子類實現純虛函數如何注冊為回調?

# re: 工廠方法Factory Methods  回復  更多評論   

2015-01-21 18:05 by eryar
@douzi
Hi douzi,

你好!

作為抽象基類的純虛函數即為實現方法。因為基類的純虛函數,所有派生的子類中必須要重新實現,否則編譯錯誤。這就是一種利用編譯器的約束。

子類實現的純虛函數的注冊方法與現在的方式一樣。

Best Regards,
Shing Liu

只有注冊用戶登錄后才能發表評論。
網站導航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            亚洲欧美激情在线视频| 亚洲在线成人精品| 亚洲国产裸拍裸体视频在线观看乱了 | 欧美国产精品v| 亚洲影院色无极综合| 欧美mv日韩mv国产网站app| 国产日本精品| 久久成人综合网| 午夜精品久久一牛影视| 国产精品久久久久aaaa| 亚洲免费综合| 亚洲一区三区视频在线观看| 欧美激情亚洲激情| 亚洲人成小说网站色在线| 麻豆精品在线视频| 久久久在线视频| 亚洲国产高清高潮精品美女| 欧美gay视频激情| 欧美成人一区二区在线| 亚洲人精品午夜| 日韩亚洲不卡在线| 欧美午夜在线| 欧美亚洲三级| 欧美在线观看视频| 今天的高清视频免费播放成人| 久热精品视频| 欧美成人一区二区三区| 久久国产视频网站| 在线一区二区三区四区| 国产精品国产三级国产专区53| 日韩视频在线观看一区二区| 最新日韩欧美| 国产精品海角社区在线观看| 亚洲四色影视在线观看| 亚洲五月六月| 精品99一区二区| 欧美电影免费观看高清完整版| 能在线观看的日韩av| av成人黄色| 亚洲一区二区视频在线| 狠狠入ady亚洲精品| 欧美成人午夜激情在线| 欧美三级免费| 久久一本综合频道| 欧美多人爱爱视频网站| 亚洲愉拍自拍另类高清精品| 性18欧美另类| 亚洲精品在线观看视频| 亚洲综合电影| 亚洲精品乱码久久久久久| av成人国产| 在线精品亚洲| 亚洲一区久久久| 在线国产亚洲欧美| 亚洲一区二区三区免费在线观看| 一区二区三区在线视频观看 | 狼狼综合久久久久综合网| 99视频有精品| 久久精品91| 亚洲视频精品在线| 久久一综合视频| 久久9热精品视频| 欧美日韩 国产精品| 久久一综合视频| 国产精品成人一区二区三区吃奶| 久久久99免费视频| 欧美性开放视频| 亚洲激情电影在线| 一色屋精品视频在线观看网站| 一区二区三区免费在线观看| 亚洲欧洲三级| 久久久久一区二区三区| 欧美亚洲三区| 国产精品高清在线| 99国产麻豆精品| 日韩亚洲国产精品| 美女主播一区| 免费在线观看日韩欧美| 国产一级揄自揄精品视频| 日韩午夜黄色| 一区二区精品| 欧美喷潮久久久xxxxx| 欧美国产精品劲爆| 永久91嫩草亚洲精品人人| 欧美一区2区三区4区公司二百| 先锋影音久久| 国产精品一区二区三区免费观看| 99国产精品久久| 亚洲图片欧洲图片av| 欧美日韩国产黄| 99精品欧美一区二区三区综合在线 | 亚洲最新在线视频| 亚洲精品网址在线观看| 麻豆av福利av久久av| 免费亚洲视频| 亚洲国产一成人久久精品| 久久一区欧美| 亚洲国产乱码最新视频| 亚洲日本国产| 欧美高清在线视频观看不卡| 亚洲成色777777女色窝| 亚洲九九九在线观看| 欧美黄色免费网站| 日韩一区二区精品在线观看| 亚洲视频axxx| 国产精品视频999| 亚洲一区精彩视频| 久久久国产午夜精品| 狠狠爱www人成狠狠爱综合网| 久久精品亚洲精品| 亚洲春色另类小说| 中文在线资源观看视频网站免费不卡| 欧美日韩成人一区| 亚洲午夜在线| 久久综合狠狠综合久久综青草| 亚洲福利视频在线| 欧美激情一二区| 亚洲伊人伊色伊影伊综合网| 久久综合狠狠综合久久综合88| 亚洲精品乱码久久久久久日本蜜臀| 欧美日韩精品久久| 欧美在线免费观看| 亚洲人成绝费网站色www| 性欧美精品高清| 亚洲风情亚aⅴ在线发布| 欧美日韩中文在线| 久久精品成人一区二区三区蜜臀| 亚洲电影免费在线观看| 亚洲综合精品四区| 一区二区视频在线观看| 欧美日韩国产精品专区| 午夜一级久久| 亚洲精品美女91| 久久久精彩视频| 亚洲私人影院在线观看| 亚洲大胆在线| 国产欧美一区二区三区视频| 欧美成人性生活| 久久不射2019中文字幕| 日韩香蕉视频| 欧美高清一区二区| 久久精品123| 亚洲综合日韩在线| 亚洲欧洲一区二区三区| 国产日韩av一区二区| 欧美日韩另类一区| 欧美1区2区3区| 久久精品九九| 亚洲自啪免费| 99v久久综合狠狠综合久久| 欧美第一黄色网| 久久一区欧美| 亚洲欧美日韩中文播放| 91久久精品国产91久久性色| 国产欧美日韩三级| 国产精品久久婷婷六月丁香| 欧美激情精品久久久六区热门| 久久久久久网| 久久久.com| 久久久精品一区二区三区| 香蕉视频成人在线观看| 亚洲综合99| 亚洲欧美色婷婷| 亚洲性xxxx| 久久综合久久88| 老鸭窝91久久精品色噜噜导演| 亚洲一区二区免费看| 日韩午夜av| 亚洲欧洲精品一区| 亚洲大黄网站| 欧美不卡视频一区| 噜噜噜噜噜久久久久久91| 久久国产精品高清| 久久不射中文字幕| 欧美在线免费观看亚洲| 欧美一区二区三区日韩视频| 亚洲欧美三级伦理| 亚洲女女女同性video| 亚洲一区二区三区四区中文| 一区二区三欧美| 亚洲视频播放| 亚洲欧美日韩一区在线| 亚洲综合不卡| 久久福利精品| 另类春色校园亚洲| 欧美激情一区二区三区| 亚洲第一黄色| 日韩视频在线观看| 亚洲一区二区三区四区五区午夜 | 亚洲一区免费视频| 亚洲一区精彩视频| 久久精品30| 欧美大香线蕉线伊人久久国产精品| 欧美激情视频在线播放 | 亚洲免费观看高清完整版在线观看熊| 亚洲日本乱码在线观看| 亚洲婷婷在线| 久久精品国产精品亚洲精品| 久久网站热最新地址| 欧美高清视频一区二区|