閔軍 羅泓
摘 要:抽象工廠模式在軟件設(shè)計中應用廣泛,但抽象工廠模式的傳統(tǒng)實現(xiàn)方式存在諸多不足。隨著技術(shù)的發(fā)展,設(shè)計模式的實現(xiàn)方式也在不斷改進。C++11新標準發(fā)布之后,涌現(xiàn)了許多改進方案。本文將在這些改進的基礎(chǔ)之上,使用C++11的lambda表達式、std::function類模板等新技術(shù),通過數(shù)據(jù)結(jié)構(gòu)和代碼結(jié)構(gòu)的優(yōu)化等方式進一步改進泛型抽象工廠設(shè)計,給出一種“新型泛型抽象工廠”的實現(xiàn)方式。實驗結(jié)果表明,該方式更為簡潔高效、復用性更強,優(yōu)雅地實現(xiàn)了對產(chǎn)品類型可變、參數(shù)可變、異類組合的支持。該實現(xiàn)方式及代碼實用性較強,可以在軟件項目中實際使用。
關(guān)鍵詞:C++11;lambda;function;泛型;抽象工廠
中圖分類號:TP311.1 文獻標識碼: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)
抽象工廠模式是最具一般性、最為抽象的一種工廠模式,由于該模式的使用有利于達到高內(nèi)聚低耦合的設(shè)計目的,因此在軟件設(shè)計中得到廣泛應用。不過,抽象工廠模式的傳統(tǒng)實現(xiàn)方式存在諸多不足,諸如實現(xiàn)復雜、類型煩瑣、類型依賴性強、可復用性弱等。隨著技術(shù)的發(fā)展,人們不斷使用多態(tài)機制、模板編程、泛型編程等技術(shù)改進設(shè)計模式[1]。C++11新標準發(fā)布之后,涌現(xiàn)了許多改進方案,比如將具體工廠構(gòu)造函數(shù)保存到關(guān)聯(lián)容器中實現(xiàn)自動注冊、使用可變參數(shù)模板和類模板實現(xiàn)泛型工廠、使用內(nèi)嵌類簡化設(shè)計等。本文將在這些改進的基礎(chǔ)之上,使用C++11的lambda表達式、std::function類模板等新技術(shù),通過數(shù)據(jù)結(jié)構(gòu)和代碼結(jié)構(gòu)的優(yōu)化等方式進一步改進泛型抽象工廠設(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實現(xiàn)泛型抽象工廠(Implement generic
abstract factory by C++11)
泛型編程技術(shù)能夠提高編程效率、實現(xiàn)非侵入性實現(xiàn),大大提高代碼復用率[3]。在設(shè)計模式的實現(xiàn)技術(shù)中,泛型編程技術(shù)是改進抽象工廠傳統(tǒng)實現(xiàn)方式的一種有效手段。通過C++11新標準泛型編程技術(shù),能夠?qū)崿F(xiàn)產(chǎn)品類型可變、參數(shù)可變、異類組合的泛型抽象工廠。圖2顯示了一種C++11實現(xiàn)的可變參數(shù)泛型抽象工廠的結(jié)構(gòu),這種實現(xiàn)方式包含兩個類模板:泛型工廠類GenericFactory、內(nèi)嵌類具體工廠注冊類Register[4]。
4 使用lambda表達式和std::function類模板設(shè)計“新型泛型抽象工廠”(Design New generic abstract factory by lambda expression and std::function class template)
上面提到的泛型抽象工廠設(shè)計方式,雖然使用了關(guān)聯(lián)容器、可變參數(shù)模板和內(nèi)嵌類等技術(shù),但也存在可以優(yōu)化的地方。下面,本文將在這些改進的基礎(chǔ)之上進一步優(yōu)化,介紹更為簡潔高效的“新型泛型抽象工廠”的實現(xiàn)方式。
4.1 “新型泛型抽象工廠”的結(jié)構(gòu)圖endprint
“新型泛型抽象工廠”的結(jié)構(gòu)如圖3所示。
4.2 用lambda表達式代替內(nèi)嵌類
lambda表達式是C++11引入的最重要、最常用的特性之一,它具有簡潔高效、聲明式編程風格、可實現(xiàn)功能閉包等優(yōu)勢[4]。前面介紹的泛型抽象工廠設(shè)計方式,使用了內(nèi)嵌類來生成具體產(chǎn)品的創(chuàng)建函數(shù)[5],其結(jié)構(gòu)圖參見圖2。在前面設(shè)計的基礎(chǔ)之上,可以用lambda表達式來代替內(nèi)嵌類,直接以lambda表達式作為具體產(chǎn)品的創(chuàng)建函數(shù),其結(jié)構(gòu)圖參見圖3,代碼可參見后面的完整代碼。兩相對比,使用lambda表達式代替內(nèi)嵌類,明顯簡化了代碼結(jié)構(gòu),不過可讀性也許會差一些。
4.3 用std::function類模板存儲和操作lambda表達式
Lambda表達式是一種匿名函數(shù)對象(或稱仿函數(shù)),其具體類型是一種依賴于具體實現(xiàn)的、唯一的函數(shù)對象類型,這種類型的名字只有編譯器才知道[6]。雖然某些簡單的lambda表達式可以直接或間接地轉(zhuǎn)換為函數(shù)指針,但一般都推薦使用auto關(guān)鍵字來標識lambda表達式的類型;若想獲取lambda表達式的具體類型,可以用C++11提供的decltype類型操作符得到;如果用戶要把lambda表達式用作參數(shù)傳遞,那就需要使用std::function對象進行捕獲。
std::function類模版是一種通用的、多態(tài)的函數(shù)封裝工具,它是對C++中現(xiàn)有各種可調(diào)用實體的一種類型安全的封裝(像函數(shù)指針這類可調(diào)用實體是類型不安全的)[7]。通過這種封裝,形成一種單一的可調(diào)用的std::function新對象,使得代碼變得簡單明了。
4.4 用std::function類模板存儲和操作具體工廠信息
在“新型泛型抽象工廠”的設(shè)計中,關(guān)鍵數(shù)據(jù)成員m_mapConFactory用于存放具體產(chǎn)品標識和具體工廠創(chuàng)建函數(shù)指針的列表信息。使用lambda表達式代替內(nèi)嵌類作為具體產(chǎn)品的創(chuàng)建函數(shù),m_mapConFactory存儲的數(shù)據(jù)類型就需要作相應改變。
m_mapConFactory中原來存儲的是具體產(chǎn)品創(chuàng)建函數(shù)的函數(shù)指針(圖2)。現(xiàn)在就不能直接保存為函數(shù)指針,而需要保存為以std::function類模板封裝的lambda表達式,具體定義參見圖3和后面的完整代碼。
5 優(yōu)化“新型泛型抽象工廠”的數(shù)據(jù)結(jié)構(gòu)(Optimize
data structure of New generic abstract factory)
為了優(yōu)化“新型泛型抽象工廠”保存的數(shù)據(jù)結(jié)構(gòu),對關(guān)鍵數(shù)據(jù)成員m_mapConFactory的結(jié)構(gòu)做了調(diào)整,具體可參見后面完整代碼中m_mapConFactory的定義。通過這種調(diào)整,再配合其他相應修改,用戶注冊具體工廠變得更為簡便,只需指定具體產(chǎn)品的類型即可完成注冊(參見后面示例)。具體工廠注冊時,“新型泛型抽象工廠”類將根據(jù)用戶提供的具體產(chǎn)品類型,自動生成唯一的具體產(chǎn)品序號、獲取其類型名稱,無須用戶再從外部輸入具體產(chǎn)品的類型標識。最后,將這些數(shù)據(jù)就地構(gòu)造為容器元素,存入m_mapConFactory容器中。
6 “新型泛型抽象工廠”的代碼優(yōu)化(Code
optimization of New generic abstract factory)
“新型泛型抽象工廠”的代碼設(shè)計,也進行了一些相關(guān)優(yōu)化。該GenericFactory類的模板參數(shù)已作簡化,只包含兩個部分:抽象產(chǎn)品類、具體產(chǎn)品構(gòu)造函數(shù)可變參數(shù)列表0—n項。這種模板參數(shù)的分配方式是比較合理的,實質(zhì)上是規(guī)定了具體產(chǎn)品構(gòu)造函數(shù)的具體類別:包括返回值和參數(shù)列表,返回值必須是AbsProduct*指針類型、參數(shù)類型和個數(shù)列表Args...args必須一致。當具體產(chǎn)品構(gòu)造函數(shù)的參數(shù)類型和個數(shù)不同時,將產(chǎn)生不同版本的GenericFactory實例。
該GenericFactory類通過靜態(tài)函數(shù)和靜態(tài)變量的方式實現(xiàn)簡單的單件模式(Singleton),各種構(gòu)造器都是私有的,不允許外部構(gòu)造。外部只能通過調(diào)用其靜態(tài)接口函數(shù)get_Instance獲取唯一的靜態(tài)實例Singleton_GenericFactory。
具體工廠注冊函數(shù)Register只有一個模板參數(shù):具體產(chǎn)品類型ConProduct,要求ConProduct類必須是AbsProduct的子類。在具體產(chǎn)品構(gòu)造函數(shù)返回值為AbsProduct*指針類型、參數(shù)列表一致的前提下,可以注冊不同實現(xiàn)細節(jié)的具體工廠。
當函數(shù)參數(shù)為常量引用時,用戶傳入臨時對象或已創(chuàng)建的變量都可以,因此,GenericFactory類的成員函數(shù)都盡量使用常量引用方式傳參,減少臨時對象的構(gòu)造和拷貝。另外,無須修改數(shù)據(jù)成員的成員函數(shù)都盡量聲明為const類型,以提高代碼的健壯性。
7 “新型泛型抽象工廠”完整實現(xiàn)代碼(Complete
implementation code of New generic abstract
factory)
下面給出本文介紹的“新型泛型抽象工廠”的完整實現(xiàn)代碼。需要注意的是,本文給出的代碼是基于C++11新標準實現(xiàn)的,必須在支持C++11新標準的編譯器中才能正常編譯使用,比如Visual Studio 2013及以上版本。
8 “新型泛型抽象工廠”的實際使用(Actual use of
New generic abstract factory)
8.1 “新型泛型抽象工廠”的使用方式
“新型泛型抽象工廠”設(shè)計比較合理周全,可以滿足抽象工廠、簡單工廠、可變參數(shù)、異類組合、具體產(chǎn)品數(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)品類型的序號;調(diào)用getStr獲取某具體產(chǎn)品類型的標識字符串;調(diào)用getSize獲取現(xiàn)有具體工廠數(shù)目。創(chuàng)建和注銷具體工廠的各種接口函數(shù)都設(shè)計了相應的重載函數(shù),用戶可以通過具體產(chǎn)品序號或具體產(chǎn)品類型字符串完成所需工作。需要注意的是,getConProduct接口返回的是容器內(nèi)部分配的堆內(nèi)存指針,用戶需要管理其生命周期,建議使用getConProduct_shared_ptr或getConProduct_unique_ptr接口,它返回的是智能指針,這樣,用戶就不用管理其生命周期。
若用戶需求比較復雜,可以通過函數(shù)封裝方式實現(xiàn)抽象工廠的需要,將一系列相關(guān)產(chǎn)品封裝在一個函數(shù)當中,實現(xiàn)一次性創(chuàng)建一系列相關(guān)產(chǎn)品的需要(參見后面示例8.3)。
8.2 具體產(chǎn)品構(gòu)造函數(shù)的參數(shù)可變
如果已經(jīng)定義了Shape基類和Rect、Circle兩個子類,便可以通過下面代碼使用“新型泛型抽象工廠”,實現(xiàn)具體工廠的注冊和具體產(chǎn)品的創(chuàng)建。Rect、Circle兩個子類的構(gòu)造函數(shù)參數(shù)可變,參數(shù)個數(shù)、類型都可以各不相同。這里,子類Rect的構(gòu)造函數(shù)有三個參數(shù)unsigned、CPoint、CPoint,子類Circle的構(gòu)造函數(shù)有兩個參數(shù)CPoint、double[9]。比如,下面代碼用于完成注冊具體工廠、創(chuàng)建具體產(chǎn)品對象并調(diào)用其Draw函數(shù)的工作:
8.3 具體工廠的異類組合
在抽象工廠應用中,經(jīng)常提到一個典型案例,跨國公司計算不同國家員工工資時可能用到異類組合的例子。假設(shè)美國員工工資包括獎金B(yǎng)onus、津貼Subsidy、稅收Tax等三個部分,中國員工工資包括獎金B(yǎng)onus、津貼Subsidy、稅收Tax、住房公積金Found等四個部分。使用本文改進的“新型泛型抽象工廠”,通過函數(shù)封裝不同抽象工廠的需要,便能更為優(yōu)雅地實現(xiàn)這種異類組合[9]。比如,下面代碼用于完成注冊具體工廠、創(chuàng)建具體產(chǎn)品對象并調(diào)用特殊業(yè)務邏輯的工作:
9 結(jié)論(Conclusion)
綜上所述,抽象工廠模式的實現(xiàn)方式一直都在不斷改進。C++11新標準發(fā)布之后,涌現(xiàn)了許多改進方案。本文在這些改進的基礎(chǔ)之上,使用C++11的lambda表達式、std::function類模板等新技術(shù),通過數(shù)據(jù)結(jié)構(gòu)和代碼結(jié)構(gòu)的優(yōu)化等方式進一步改進泛型抽象工廠設(shè)計,給出了一種更為簡潔高效的“新型泛型抽象工廠”的實現(xiàn)方式。實驗結(jié)果表明,該方式更為簡潔高效、復用性更強,優(yōu)雅地實現(xiàn)了對產(chǎn)品類型可變、參數(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] 祁宇.深入應用C++11:代碼優(yōu)化與工程級應用[M].北京:機械工業(yè)出版社,2015.
[5] 閔軍,羅泓.C++11實現(xiàn)可變參數(shù)泛型抽象工廠[J].軟件工程,2017,20(05):18-22.
[6] B Michael Wong(加),IBM XL編譯器中國開發(fā)團隊,著.深入理解C++11:C++11新特性解析與應用[M].北京:機械工業(yè)出版社,2013.
[7] Marc Gregoire(美),著.張永強,譯.C++高級編程(第3版)[M].北京:清華大學出版社,2015:519-521.
[8] B Gamma Erich(美),等,著.李英軍,等,譯.設(shè)計模式:可復用面向?qū)ο筌浖幕A(chǔ)[M].北京:機械工業(yè)出版社,2000.
[9] B Joshua Kerievsky(美).楊光,劉基誠,譯.重構(gòu)與模式(修訂版)[M].北京:人民郵電出版社,2013:51-59.
作者簡介:
閔 軍(1966-),男,碩士,研究員.研究領(lǐng)域:C++程序設(shè)計,設(shè)計模式,計算機網(wǎng)絡(luò).
羅 泓(1970-),女,專科,工程師.研究領(lǐng)域:數(shù)據(jù)分析與處理,電路設(shè)計,信息管理系統(tǒng).endprint