閔軍 羅泓
摘 要:抽象工廠模式在軟件設(shè)計(jì)中應(yīng)用廣泛,但抽象工廠模式的傳統(tǒng)實(shí)現(xiàn)方式存在諸多不足。隨著技術(shù)的發(fā)展,設(shè)計(jì)模式的實(shí)現(xiàn)方式也在不斷改進(jìn)。C++11新標(biāo)準(zhǔn)發(fā)布之后,涌現(xiàn)了許多改進(jìn)方案。本文將在這些改進(jìn)的基礎(chǔ)之上,使用C++11的lambda表達(dá)式、std::function類模板等新技術(shù),通過數(shù)據(jù)結(jié)構(gòu)和代碼結(jié)構(gòu)的優(yōu)化等方式進(jìn)一步改進(jìn)泛型抽象工廠設(shè)計(jì),給出一種“新型泛型抽象工廠”的實(shí)現(xiàn)方式。實(shí)驗(yàn)結(jié)果表明,該方式更為簡潔高效、復(fù)用性更強(qiáng),優(yōu)雅地實(shí)現(xiàn)了對(duì)產(chǎn)品類型可變、參數(shù)可變、異類組合的支持。該實(shí)現(xiàn)方式及代碼實(shí)用性較強(qiáng),可以在軟件項(xiàng)目中實(shí)際使用。
關(guān)鍵詞:C++11;lambda;function;泛型;抽象工廠
中圖分類號(hào):TP311.1 文獻(xiàn)標(biāo)識(shí)碼:A
Abstract:The abstract factory pattern has been widely used in software design,but there are still some shortcomings in the traditional implementation of the abstract factory design pattern.With the development of technology,the implementation of design patterns is constantly improving.After the release of new C++11 standards,many improvement have emerged.Based on these improvements,this paper further improves the generic abstract factory design by adopting the new technology of the C++11 lambda expression and the std::function class template,optimizing the data structure and the code structure.The implementation model of New Generic Abstract Factory is proposed.Experimental results show that this model is more concise and efficient with better reusability.This model can gracefully implement the support to variable product types,variable parameters,and heterogeneous combinations.With decent practicality,this implementation method and code can be actually applied in software projects.
Keywords:C++11;lambda;function;generic;abstract factory
1 引言(Introduction)
抽象工廠模式是最具一般性、最為抽象的一種工廠模式,由于該模式的使用有利于達(dá)到高內(nèi)聚低耦合的設(shè)計(jì)目的,因此在軟件設(shè)計(jì)中得到廣泛應(yīng)用。不過,抽象工廠模式的傳統(tǒng)實(shí)現(xiàn)方式存在諸多不足,諸如實(shí)現(xiàn)復(fù)雜、類型煩瑣、類型依賴性強(qiáng)、可復(fù)用性弱等。隨著技術(shù)的發(fā)展,人們不斷使用多態(tài)機(jī)制、模板編程、泛型編程等技術(shù)改進(jìn)設(shè)計(jì)模式[1]。C++11新標(biāo)準(zhǔn)發(fā)布之后,涌現(xiàn)了許多改進(jìn)方案,比如將具體工廠構(gòu)造函數(shù)保存到關(guān)聯(lián)容器中實(shí)現(xiàn)自動(dòng)注冊、使用可變參數(shù)模板和類模板實(shí)現(xiàn)泛型工廠、使用內(nèi)嵌類簡化設(shè)計(jì)等。本文將在這些改進(jìn)的基礎(chǔ)之上,使用C++11的lambda表達(dá)式、std::function類模板等新技術(shù),通過數(shù)據(jù)結(jié)構(gòu)和代碼結(jié)構(gòu)的優(yōu)化等方式進(jìn)一步改進(jìn)泛型抽象工廠設(shè)計(jì),給出一種更為簡潔高效的“新型泛型抽象工廠”的實(shí)現(xiàn)方式。
2 抽象工廠模式(Abstract factory pattern)
抽象工廠模式屬于創(chuàng)建型模式,簡單地說,抽象工廠模式就是用于完成“多系列相互依賴的具體產(chǎn)品”的創(chuàng)建工作,避免客戶程序和這種“多系列具體產(chǎn)品創(chuàng)建工作”的緊密耦合[2]。抽象工廠模式結(jié)構(gòu)如圖1所示。
3 C++11實(shí)現(xiàn)泛型抽象工廠(Implement generic
abstract factory by C++11)
泛型編程技術(shù)能夠提高編程效率、實(shí)現(xiàn)非侵入性實(shí)現(xiàn),大大提高代碼復(fù)用率[3]。在設(shè)計(jì)模式的實(shí)現(xiàn)技術(shù)中,泛型編程技術(shù)是改進(jìn)抽象工廠傳統(tǒng)實(shí)現(xiàn)方式的一種有效手段。通過C++11新標(biāo)準(zhǔn)泛型編程技術(shù),能夠?qū)崿F(xiàn)產(chǎn)品類型可變、參數(shù)可變、異類組合的泛型抽象工廠。圖2顯示了一種C++11實(shí)現(xiàn)的可變參數(shù)泛型抽象工廠的結(jié)構(gòu),這種實(shí)現(xiàn)方式包含兩個(gè)類模板:泛型工廠類GenericFactory、內(nèi)嵌類具體工廠注冊類Register[4]。
4 使用lambda表達(dá)式和std::function類模板設(shè)計(jì)“新型泛型抽象工廠”(Design New generic abstract factory by lambda expression and std::function class template)
上面提到的泛型抽象工廠設(shè)計(jì)方式,雖然使用了關(guān)聯(lián)容器、可變參數(shù)模板和內(nèi)嵌類等技術(shù),但也存在可以優(yōu)化的地方。下面,本文將在這些改進(jìn)的基礎(chǔ)之上進(jìn)一步優(yōu)化,介紹更為簡潔高效的“新型泛型抽象工廠”的實(shí)現(xiàn)方式。
4.1 “新型泛型抽象工廠”的結(jié)構(gòu)圖endprint
“新型泛型抽象工廠”的結(jié)構(gòu)如圖3所示。
4.2 用lambda表達(dá)式代替內(nèi)嵌類
lambda表達(dá)式是C++11引入的最重要、最常用的特性之一,它具有簡潔高效、聲明式編程風(fēng)格、可實(shí)現(xiàn)功能閉包等優(yōu)勢[4]。前面介紹的泛型抽象工廠設(shè)計(jì)方式,使用了內(nèi)嵌類來生成具體產(chǎn)品的創(chuàng)建函數(shù)[5],其結(jié)構(gòu)圖參見圖2。在前面設(shè)計(jì)的基礎(chǔ)之上,可以用lambda表達(dá)式來代替內(nèi)嵌類,直接以lambda表達(dá)式作為具體產(chǎn)品的創(chuàng)建函數(shù),其結(jié)構(gòu)圖參見圖3,代碼可參見后面的完整代碼。兩相對(duì)比,使用lambda表達(dá)式代替內(nèi)嵌類,明顯簡化了代碼結(jié)構(gòu),不過可讀性也許會(huì)差一些。
4.3 用std::function類模板存儲(chǔ)和操作lambda表達(dá)式
Lambda表達(dá)式是一種匿名函數(shù)對(duì)象(或稱仿函數(shù)),其具體類型是一種依賴于具體實(shí)現(xiàn)的、唯一的函數(shù)對(duì)象類型,這種類型的名字只有編譯器才知道[6]。雖然某些簡單的lambda表達(dá)式可以直接或間接地轉(zhuǎn)換為函數(shù)指針,但一般都推薦使用auto關(guān)鍵字來標(biāo)識(shí)lambda表達(dá)式的類型;若想獲取lambda表達(dá)式的具體類型,可以用C++11提供的decltype類型操作符得到;如果用戶要把lambda表達(dá)式用作參數(shù)傳遞,那就需要使用std::function對(duì)象進(jìn)行捕獲。
std::function類模版是一種通用的、多態(tài)的函數(shù)封裝工具,它是對(duì)C++中現(xiàn)有各種可調(diào)用實(shí)體的一種類型安全的封裝(像函數(shù)指針這類可調(diào)用實(shí)體是類型不安全的)[7]。通過這種封裝,形成一種單一的可調(diào)用的std::function新對(duì)象,使得代碼變得簡單明了。
4.4 用std::function類模板存儲(chǔ)和操作具體工廠信息
在“新型泛型抽象工廠”的設(shè)計(jì)中,關(guān)鍵數(shù)據(jù)成員m_mapConFactory用于存放具體產(chǎn)品標(biāo)識(shí)和具體工廠創(chuàng)建函數(shù)指針的列表信息。使用lambda表達(dá)式代替內(nèi)嵌類作為具體產(chǎn)品的創(chuàng)建函數(shù),m_mapConFactory存儲(chǔ)的數(shù)據(jù)類型就需要作相應(yīng)改變。
m_mapConFactory中原來存儲(chǔ)的是具體產(chǎn)品創(chuàng)建函數(shù)的函數(shù)指針(圖2)。現(xiàn)在就不能直接保存為函數(shù)指針,而需要保存為以std::function類模板封裝的lambda表達(dá)式,具體定義參見圖3和后面的完整代碼。
5 優(yōu)化“新型泛型抽象工廠”的數(shù)據(jù)結(jié)構(gòu)(Optimize
data structure of New generic abstract factory)
為了優(yōu)化“新型泛型抽象工廠”保存的數(shù)據(jù)結(jié)構(gòu),對(duì)關(guān)鍵數(shù)據(jù)成員m_mapConFactory的結(jié)構(gòu)做了調(diào)整,具體可參見后面完整代碼中m_mapConFactory的定義。通過這種調(diào)整,再配合其他相應(yīng)修改,用戶注冊具體工廠變得更為簡便,只需指定具體產(chǎn)品的類型即可完成注冊(參見后面示例)。具體工廠注冊時(shí),“新型泛型抽象工廠”類將根據(jù)用戶提供的具體產(chǎn)品類型,自動(dòng)生成唯一的具體產(chǎn)品序號(hào)、獲取其類型名稱,無須用戶再從外部輸入具體產(chǎn)品的類型標(biāo)識(shí)。最后,將這些數(shù)據(jù)就地構(gòu)造為容器元素,存入m_mapConFactory容器中。
6 “新型泛型抽象工廠”的代碼優(yōu)化(Code
optimization of New generic abstract factory)
“新型泛型抽象工廠”的代碼設(shè)計(jì),也進(jìn)行了一些相關(guān)優(yōu)化。該GenericFactory類的模板參數(shù)已作簡化,只包含兩個(gè)部分:抽象產(chǎn)品類、具體產(chǎn)品構(gòu)造函數(shù)可變參數(shù)列表0—n項(xiàng)。這種模板參數(shù)的分配方式是比較合理的,實(shí)質(zhì)上是規(guī)定了具體產(chǎn)品構(gòu)造函數(shù)的具體類別:包括返回值和參數(shù)列表,返回值必須是AbsProduct*指針類型、參數(shù)類型和個(gè)數(shù)列表Args...args必須一致。當(dāng)具體產(chǎn)品構(gòu)造函數(shù)的參數(shù)類型和個(gè)數(shù)不同時(shí),將產(chǎn)生不同版本的GenericFactory實(shí)例。
該GenericFactory類通過靜態(tài)函數(shù)和靜態(tài)變量的方式實(shí)現(xiàn)簡單的單件模式(Singleton),各種構(gòu)造器都是私有的,不允許外部構(gòu)造。外部只能通過調(diào)用其靜態(tài)接口函數(shù)get_Instance獲取唯一的靜態(tài)實(shí)例Singleton_GenericFactory。
具體工廠注冊函數(shù)Register只有一個(gè)模板參數(shù):具體產(chǎn)品類型ConProduct,要求ConProduct類必須是AbsProduct的子類。在具體產(chǎn)品構(gòu)造函數(shù)返回值為AbsProduct*指針類型、參數(shù)列表一致的前提下,可以注冊不同實(shí)現(xiàn)細(xì)節(jié)的具體工廠。
當(dāng)函數(shù)參數(shù)為常量引用時(shí),用戶傳入臨時(shí)對(duì)象或已創(chuàng)建的變量都可以,因此,GenericFactory類的成員函數(shù)都盡量使用常量引用方式傳參,減少臨時(shí)對(duì)象的構(gòu)造和拷貝。另外,無須修改數(shù)據(jù)成員的成員函數(shù)都盡量聲明為const類型,以提高代碼的健壯性。
7 “新型泛型抽象工廠”完整實(shí)現(xiàn)代碼(Complete
implementation code of New generic abstract
factory)
下面給出本文介紹的“新型泛型抽象工廠”的完整實(shí)現(xiàn)代碼。需要注意的是,本文給出的代碼是基于C++11新標(biāo)準(zhǔn)實(shí)現(xiàn)的,必須在支持C++11新標(biāo)準(zhǔn)的編譯器中才能正常編譯使用,比如Visual Studio 2013及以上版本。
8 “新型泛型抽象工廠”的實(shí)際使用(Actual use of
New generic abstract factory)
8.1 “新型泛型抽象工廠”的使用方式
“新型泛型抽象工廠”設(shè)計(jì)比較合理周全,可以滿足抽象工廠、簡單工廠、可變參數(shù)、異類組合、具體產(chǎn)品數(shù)量繁多等情況的實(shí)現(xiàn)需求[8]。使用也很簡單,首先創(chuàng)建各種具體工廠,方法就是通過GenericFactory::get_Instance調(diào)用其Register注冊函數(shù),將各種具體工廠的創(chuàng)建函數(shù)指針存入m_mapConFactory容器中。接下來,用戶便可以通過GenericFactory::get_Instance調(diào)用各種公共接口函數(shù),更為靈活方便地使用各種功能。endprint
用戶可以調(diào)用getNum獲取某具體產(chǎn)品類型的序號(hào);調(diào)用getStr獲取某具體產(chǎn)品類型的標(biāo)識(shí)字符串;調(diào)用getSize獲取現(xiàn)有具體工廠數(shù)目。創(chuàng)建和注銷具體工廠的各種接口函數(shù)都設(shè)計(jì)了相應(yīng)的重載函數(shù),用戶可以通過具體產(chǎn)品序號(hào)或具體產(chǎn)品類型字符串完成所需工作。需要注意的是,getConProduct接口返回的是容器內(nèi)部分配的堆內(nèi)存指針,用戶需要管理其生命周期,建議使用getConProduct_shared_ptr或getConProduct_unique_ptr接口,它返回的是智能指針,這樣,用戶就不用管理其生命周期。
若用戶需求比較復(fù)雜,可以通過函數(shù)封裝方式實(shí)現(xiàn)抽象工廠的需要,將一系列相關(guān)產(chǎn)品封裝在一個(gè)函數(shù)當(dāng)中,實(shí)現(xiàn)一次性創(chuàng)建一系列相關(guān)產(chǎn)品的需要(參見后面示例8.3)。
8.2 具體產(chǎn)品構(gòu)造函數(shù)的參數(shù)可變
如果已經(jīng)定義了Shape基類和Rect、Circle兩個(gè)子類,便可以通過下面代碼使用“新型泛型抽象工廠”,實(shí)現(xiàn)具體工廠的注冊和具體產(chǎn)品的創(chuàng)建。Rect、Circle兩個(gè)子類的構(gòu)造函數(shù)參數(shù)可變,參數(shù)個(gè)數(shù)、類型都可以各不相同。這里,子類Rect的構(gòu)造函數(shù)有三個(gè)參數(shù)unsigned、CPoint、CPoint,子類Circle的構(gòu)造函數(shù)有兩個(gè)參數(shù)CPoint、double[9]。比如,下面代碼用于完成注冊具體工廠、創(chuàng)建具體產(chǎn)品對(duì)象并調(diào)用其Draw函數(shù)的工作:
8.3 具體工廠的異類組合
在抽象工廠應(yīng)用中,經(jīng)常提到一個(gè)典型案例,跨國公司計(jì)算不同國家員工工資時(shí)可能用到異類組合的例子。假設(shè)美國員工工資包括獎(jiǎng)金B(yǎng)onus、津貼Subsidy、稅收Tax等三個(gè)部分,中國員工工資包括獎(jiǎng)金B(yǎng)onus、津貼Subsidy、稅收Tax、住房公積金Found等四個(gè)部分。使用本文改進(jìn)的“新型泛型抽象工廠”,通過函數(shù)封裝不同抽象工廠的需要,便能更為優(yōu)雅地實(shí)現(xiàn)這種異類組合[9]。比如,下面代碼用于完成注冊具體工廠、創(chuàng)建具體產(chǎn)品對(duì)象并調(diào)用特殊業(yè)務(wù)邏輯的工作:
9 結(jié)論(Conclusion)
綜上所述,抽象工廠模式的實(shí)現(xiàn)方式一直都在不斷改進(jìn)。C++11新標(biāo)準(zhǔn)發(fā)布之后,涌現(xiàn)了許多改進(jìn)方案。本文在這些改進(jìn)的基礎(chǔ)之上,使用C++11的lambda表達(dá)式、std::function類模板等新技術(shù),通過數(shù)據(jù)結(jié)構(gòu)和代碼結(jié)構(gòu)的優(yōu)化等方式進(jìn)一步改進(jìn)泛型抽象工廠設(shè)計(jì),給出了一種更為簡潔高效的“新型泛型抽象工廠”的實(shí)現(xiàn)方式。實(shí)驗(yàn)結(jié)果表明,該方式更為簡潔高效、復(fù)用性更強(qiáng),優(yōu)雅地實(shí)現(xiàn)了對(duì)產(chǎn)品類型可變、參數(shù)可變、異類組合的支持。該實(shí)現(xiàn)方式及代碼實(shí)用性較強(qiáng),可以在軟件項(xiàng)目中實(shí)際使用。
參考文獻(xiàn)(References)
[1] Bemardi ML,Cimitile M,Lucca GD.Design pattem detection using a DSL—driven graph matching approach[J].Journal of Software Evolution&Process,2014,26(12):1233-1266.
[2] B Rasool G,Mader P.A customizable approach to design pattems recognition based on feature types[J].Arabian Journal for Science&Engineering,2014,39(12):8851-8873.
[3] Stephen Prata.C++ Primer Plus,Sixth Edition[M].USA:Addison-Wesley Professional,2011.
[4] 祁宇.深入應(yīng)用C++11:代碼優(yōu)化與工程級(jí)應(yīng)用[M].北京:機(jī)械工業(yè)出版社,2015.
[5] 閔軍,羅泓.C++11實(shí)現(xiàn)可變參數(shù)泛型抽象工廠[J].軟件工程,2017,20(05):18-22.
[6] B Michael Wong(加),IBM XL編譯器中國開發(fā)團(tuán)隊(duì),著.深入理解C++11:C++11新特性解析與應(yīng)用[M].北京:機(jī)械工業(yè)出版社,2013.
[7] Marc Gregoire(美),著.張永強(qiáng),譯.C++高級(jí)編程(第3版)[M].北京:清華大學(xué)出版社,2015:519-521.
[8] B Gamma Erich(美),等,著.李英軍,等,譯.設(shè)計(jì)模式:可復(fù)用面向?qū)ο筌浖幕A(chǔ)[M].北京:機(jī)械工業(yè)出版社,2000.
[9] B Joshua Kerievsky(美).楊光,劉基誠,譯.重構(gòu)與模式(修訂版)[M].北京:人民郵電出版社,2013:51-59.
作者簡介:
閔 軍(1966-),男,碩士,研究員.研究領(lǐng)域:C++程序設(shè)計(jì),設(shè)計(jì)模式,計(jì)算機(jī)網(wǎng)絡(luò).
羅 泓(1970-),女,??疲こ處?研究領(lǐng)域:數(shù)據(jù)分析與處理,電路設(shè)計(jì),信息管理系統(tǒng).endprint