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

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) 評(píng)論(4)  編輯 收藏 引用

Factory Methods

工廠方法

eryar@163.com

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

關(guān)鍵字Key Words:C++、Factory Pattern、

一、概述 Overview

工廠方法是創(chuàng)建型模式,允許在不指定需要?jiǎng)?chuàng)建對(duì)象類型的情況下創(chuàng)建出對(duì)象。本質(zhì)上來說,工廠方法就是一個(gè)通用的構(gòu)造函數(shù)。C++中的構(gòu)造函數(shù)有以下幾種限制:

l 無返回值(No return result)。在構(gòu)造函數(shù)中不能返回一個(gè)值。這就意味著:例如當(dāng)構(gòu)造失敗時(shí)不能返回一個(gè)NULL作為初始化失敗的信號(hào)。

l 命名有約束(Constrained naming)。構(gòu)造函數(shù)還是很好識(shí)別的,因?yàn)樗拿仨毰c類名一樣。

l 靜態(tài)綁定創(chuàng)建的(Statically bound creation)。當(dāng)創(chuàng)建一個(gè)對(duì)象時(shí),必須指定一個(gè)在編譯時(shí)就能確定的類名。如:Foo *f = new Foo(),F(xiàn)oo就是編譯器必須知道的類名。C++的構(gòu)造函數(shù)沒有運(yùn)行時(shí)的動(dòng)態(tài)綁定功能(dynamic binding at run time)。

l 無虛構(gòu)造函數(shù)(No virtual constructors)。在C++中不能聲明虛的構(gòu)造函數(shù),必須指定在編譯時(shí)能確定的類型。編譯器據(jù)此為指定的類型分配內(nèi)存,然后調(diào)用基類的默認(rèn)構(gòu)造函數(shù),再調(diào)用指定類的構(gòu)造函數(shù)。這就是不能定義構(gòu)造函數(shù)為虛函數(shù)的原因。

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

二、抽象基類 Abstract Base Classes

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

#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

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

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

當(dāng)然,抽象基類中并不是所有的方法都必須是純虛函數(shù),也可以實(shí)現(xiàn)一些函數(shù)。

當(dāng)任意一個(gè)類有一個(gè)或多個(gè)虛函數(shù)時(shí),通常會(huì)把抽象基類的析構(gòu)函數(shù)聲明為虛函數(shù)。如下代碼說明了這樣做的重要性:

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

 

三、簡(jiǎn)單工廠模式 Simple Factory Example

在復(fù)習(xí)了抽象基類后,讓我們?cè)诤?jiǎn)單工廠方法中使用它。繼續(xù)以renderer.h為例,聲明創(chuàng)建工廠,創(chuàng)建的對(duì)象類型為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

這里只聲明了一個(gè)工廠方法:它只是一個(gè)普通的函數(shù),返回值是對(duì)象的實(shí)例。注意到這個(gè)方法不能返回一個(gè)指定類型的IRender實(shí)例,因?yàn)槌橄蠡愂遣荒鼙粚?shí)例化的。但是它可以返回派生類的實(shí)例。當(dāng)然,你可以使用字符串作為參數(shù)來指定需要?jiǎng)?chuàng)建對(duì)象的類型。

假設(shè)已經(jīng)實(shí)現(xiàn)了派生自IRender的三個(gè)具體類:IRenderer::OpenGLRenderer,DirectXRenderer、MesaRenderer。再假設(shè)你不想讓使用API的用戶知道可以創(chuàng)建哪些類型:他們必須完全隱藏在API后面。基于這些條件,可以實(shí)現(xiàn)工廠方法的程序如下:

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

這個(gè)工廠方法可以返回IRenderer的三個(gè)派生類之一的實(shí)例,取決于傳入的參數(shù)字符串。這就可以讓用戶決定在運(yùn)行時(shí)而不是在編譯時(shí)創(chuàng)建哪個(gè)派生類,這與普通的構(gòu)造函數(shù)要求一致。這樣做是有很多好處的,因?yàn)樗梢愿鶕?jù)用戶輸入或根據(jù)運(yùn)行時(shí)讀入的配置文件內(nèi)容來創(chuàng)建不同的對(duì)象。

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

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

四、擴(kuò)展工廠模式 Extensible Factory Example

為了讓工派生類從工廠方法中解耦,且允許在運(yùn)行時(shí)添加新的派生類,可以去維護(hù)包含類型及與類型創(chuàng)建關(guān)聯(lián)的函數(shù)的映射(map)來更新一下工廠類。可以通過添加幾個(gè)新的函數(shù)用來注冊(cè)與注銷新的派生類。在運(yùn)行時(shí)能注冊(cè)新的類允許這種類型的工廠方法模式可用于創(chuàng)建可擴(kuò)展的接口。

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

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

使用工廠對(duì)象創(chuàng)建派生類的方法如下所示:

#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的用戶可以在系統(tǒng)中注冊(cè)與注銷一個(gè)新的渲染器。編譯器將會(huì)確保用戶定義的新的渲染器必須實(shí)現(xiàn)抽象基類IRenderer的所有抽象接口,即新的渲染器類必須實(shí)現(xiàn)抽象基類IRenderer所有的純虛函數(shù)。如下代碼演示了用戶如何自定義新的渲染器,在工廠對(duì)象中注冊(cè),并叫工廠對(duì)象為之創(chuàng)建一個(gè)實(shí)例:

這里需要注意的一點(diǎn)是我向類UserRenderer中添加了一個(gè)Create()函數(shù),這是因?yàn)楣S對(duì)象的注冊(cè)方法需要返回一個(gè)對(duì)象的回調(diào)函數(shù)。這個(gè)回調(diào)函數(shù)不一定必須是抽象基類IRenderer的一部分,它可以是一個(gè)自由的函數(shù)。但是向抽象基類IRenderer中添加這個(gè)函數(shù)是一個(gè)好習(xí)慣,這樣就確保了所有相關(guān)功能的一致性。實(shí)際上,為了強(qiáng)調(diào)這種約定,可以將Create作為抽象基類IRenderer的一個(gè)純虛函數(shù)。

五、結(jié)論 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[未登錄]  回復(fù)  更多評(píng)論   

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

# re: 工廠方法Factory Methods  回復(fù)  更多評(píng)論   

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

# re: 工廠方法Factory Methods  回復(fù)  更多評(píng)論   

2015-01-20 23:55 by douzi
實(shí)際上,為了強(qiáng)調(diào)這種約定,可以將Create作為抽象基類IRenderer的一個(gè)純虛函數(shù)。
請(qǐng)問,這一句的方法,如何實(shí)現(xiàn)?
子類實(shí)現(xiàn)純虛函數(shù)如何注冊(cè)為回調(diào)?

# re: 工廠方法Factory Methods  回復(fù)  更多評(píng)論   

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

你好!

作為抽象基類的純虛函數(shù)即為實(shí)現(xiàn)方法。因?yàn)榛惖募兲摵瘮?shù),所有派生的子類中必須要重新實(shí)現(xiàn),否則編譯錯(cuò)誤。這就是一種利用編譯器的約束。

子類實(shí)現(xiàn)的純虛函數(shù)的注冊(cè)方法與現(xiàn)在的方式一樣。

Best Regards,
Shing Liu

只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。
網(wǎng)站導(dǎo)航: 博客園   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>
            国产一区二区电影在线观看| 欧美在线视频a| 欧美视频二区36p| 欧美激情在线免费观看| 久久xxxx| 欧美一级大片在线观看| 欧美在线国产精品| 久久精品国产精品亚洲| 久色婷婷小香蕉久久| 美日韩免费视频| 欧美日韩国产综合一区二区| 国产精品久久久久毛片大屁完整版| 欧美日韩在线综合| 国产日韩精品久久| 最新日韩在线视频| 亚洲一级在线| 久久精品视频免费观看| 免费欧美视频| 日韩视频免费| 欧美一区午夜精品| 毛片基地黄久久久久久天堂| 欧美成人免费va影院高清| 亚洲精品视频在线观看免费| 午夜影院日韩| 欧美区一区二| 精品电影一区| 亚洲一区二区三区免费在线观看| 欧美一区二区国产| 最新热久久免费视频| 亚洲剧情一区二区| 久久久一二三| 国产精品热久久久久夜色精品三区| 激情成人综合| 销魂美女一区二区三区视频在线| 免费观看30秒视频久久| 99精品99| 免播放器亚洲一区| 国产私拍一区| 亚洲免费在线观看视频| 亚洲高清不卡在线| 欧美一区二区三区四区视频 | 久久精品一级爱片| 亚洲精品小视频在线观看| 久久精品卡一| 国产精品jvid在线观看蜜臀 | 久久精品视频在线看| 欧美一级午夜免费电影| 欧美1级日本1级| 日韩一级大片在线| 久久人人精品| 国产亚洲高清视频| 销魂美女一区二区三区视频在线| 亚洲人成欧美中文字幕| 久久在线播放| 黄色av一区| 久久久欧美一区二区| 亚洲男女自偷自拍| 国产精品老牛| 性欧美在线看片a免费观看| 一本色道久久综合亚洲精品婷婷| 欧美激情精品久久久六区热门| 狠狠色综合色区| 看欧美日韩国产| 久久久在线视频| 亚洲成人在线观看视频| 麻豆久久精品| 麻豆成人综合网| 91久久在线| 最新中文字幕亚洲| 欧美日韩久久精品| 宅男噜噜噜66国产日韩在线观看| 亚洲人体大胆视频| 欧美日韩网站| 欧美亚洲在线播放| 欧美一区二区三区在线看| 国产一区二区0| 欧美成人情趣视频| 在线视频中文亚洲| 精品91免费| 欧美激情视频在线播放| 蜜臀av一级做a爰片久久 | 久久综合伊人77777麻豆| 亚洲国产黄色片| 亚洲人成在线免费观看| 欧美日韩精品久久久| 亚洲在线中文字幕| 欧美中文字幕在线播放| 亚洲国产片色| 一本色道久久88精品综合| 国产欧美日韩激情| 免费一级欧美在线大片| 欧美日韩精品免费看| 欧美中文日韩| 欧美成人精品h版在线观看| 日韩午夜在线视频| 午夜精品久久久久久久久久久久| 在线国产精品一区| 亚洲伦伦在线| 国模精品一区二区三区| 亚洲欧洲在线观看| 国产日韩欧美视频| 亚洲人成在线免费观看| 国产一区二区三区奇米久涩 | 性做久久久久久免费观看欧美| 亚洲一级特黄| 在线国产亚洲欧美| 牛牛精品成人免费视频| 欧美午夜精品久久久| 久久久久国产精品麻豆ai换脸| 欧美77777| 久久久久久久久久久久久女国产乱| 欧美精品一区二区久久婷婷| 久久久久久久尹人综合网亚洲| 欧美日韩精选| 免费欧美日韩国产三级电影| 国产欧美一区二区三区另类精品| 亚洲伦理在线观看| 最新亚洲一区| 欧美中文在线观看| 新67194成人永久网站| 欧美日韩成人一区| 欧美岛国激情| 激情婷婷亚洲| 亚洲摸下面视频| 亚洲免费伊人电影在线观看av| 免费欧美视频| 蜜桃av噜噜一区二区三区| 国产视频久久久久久久| 99视频一区二区三区| 99re66热这里只有精品4| 六月婷婷久久| 免费不卡在线观看| 精品51国产黑色丝袜高跟鞋| 亚洲欧美日韩另类| 欧美一区二区三区四区高清 | 亚洲狼人精品一区二区三区| 久久久水蜜桃av免费网站| 亚洲综合第一页| 欧美精品电影| 亚洲精选中文字幕| 中文成人激情娱乐网| 欧美激情第10页| 亚洲国产激情| 日韩亚洲欧美一区| 欧美母乳在线| 99国产精品自拍| 亚洲欧美日韩第一区| 国产精品视频免费观看| 亚洲欧美国产精品桃花| 久久久国产一区二区| 激情一区二区三区| 蜜桃精品久久久久久久免费影院| 老色批av在线精品| 亚洲精品乱码久久久久久日本蜜臀 | 美国十次成人| 欧美国产综合视频| 亚洲免费激情| 国产精品久久久久久久一区探花 | 免费黄网站欧美| 国产亚洲精品一区二区| 久久综合九色综合欧美就去吻| 久久综合中文| 亚洲每日更新| 国产精品蜜臀在线观看| 欧美中文在线视频| 免费人成精品欧美精品| 影音先锋国产精品| 欧美成人精品不卡视频在线观看 | 亚洲欧美日韩精品一区二区| 久久久久久自在自线| 亚洲人成人一区二区在线观看| 欧美日韩麻豆| 久久精品国产v日韩v亚洲| 亚洲国产高清自拍| 性欧美videos另类喷潮| 在线日韩欧美| 国产精品福利在线观看| 久久er精品视频| 亚洲美女视频网| 久久亚洲精品欧美| 亚洲私人影院| 亚洲国产精品精华液网站| 欧美视频在线观看一区| 欧美在线看片a免费观看| 亚洲欧洲日韩女同| 开心色5月久久精品| 亚洲欧美日本伦理| 亚洲精选成人| 黄色日韩网站视频| 国产精品热久久久久夜色精品三区| 久久久久在线| 午夜精品一区二区三区电影天堂| 亚洲国产婷婷香蕉久久久久久99 | 欧美性色综合| 蜜桃av久久久亚洲精品| 香蕉久久一区二区不卡无毒影院| 亚洲精品在线看| 欧美暴力喷水在线| 亚洲欧美激情一区| 99精品视频一区|