日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

強(qiáng)大的C ——把乘法變成加法

 ShangShujie 2007-05-02
把乘法變成加法

不要誤會,不是用加法重載operator*。(做這種事情的程序員應(yīng)該立刻開除)?;蛘呷魏胃嬎阌嘘P(guān)的事。這里要講的是另外一個故事。
當(dāng) 你看我這篇帖子的時候,是否想過你的計算機(jī)是如何構(gòu)成的?內(nèi)存、主板、硬盤、cpu、顯卡、顯示器、光驅(qū)、鍵盤、鼠標(biāo)等等。沒錯,你肯定很熟悉了。那么, 你是否想過電腦廠商為了生產(chǎn)不同的配置的計算機(jī),準(zhǔn)備了多少配件嗎?不好意思,我也不清楚。不過沒關(guān)系,我們可以假設(shè)。假設(shè)內(nèi)存規(guī)格有256、512、 1G、2G四種規(guī)格(不考慮牌號,后面也一樣);硬盤規(guī)格有80G、100G、120G、160G和200G五種規(guī)格;顯卡有三種(假設(shè)一下,我搞不清現(xiàn) 在有多少種顯卡);cpu有五款;顯示器有4種;光驅(qū)有5種;鼠標(biāo)鍵盤就不考慮了。
那么我們總共可以得到多少種配置呢?很簡單,4*5*3*5*4*5=6000種!當(dāng)然沒有哪個廠商會推出6000款型號,只是假設(shè)一下。那么總共有多少配件呢?4+5+3+5+4+5=26種。也就是廠商只需管理26種配件,便可以制造出6000個機(jī)型。
現(xiàn)在讓我們再假設(shè)一下,當(dāng)初IBM發(fā)明PC的時候,一時糊涂,沒有把PC的各個組成部分組件化,所有的組成部分都是焊在一塊電路板上的,包括顯示器。那么如果一個廠商想獲得這6000種配置的電腦,那么他們就必須直接生產(chǎn),并且管理6000種不同的組件(電腦)。
這就是差別,組件化vs非組件化:26對6000。
好, 現(xiàn)在回到我們熟悉的軟件(開發(fā))上來。我們在軟件開發(fā)是通常也面臨計算機(jī)廠商同樣的問題:產(chǎn)品多樣性的問題。即便是同一種軟件,在不同的客戶那里通常會有 不同要求。為每一個客戶開發(fā)不同的軟件,明顯是非常低效的。(不幸的是,這種愚蠢的行為,在業(yè)界幾乎成了慣例)。順便說明一下,這里的客戶是指用你軟件的 人。如果你開發(fā)的是庫,那么客戶就是使用你的庫的人。而本文主要針對的是庫開發(fā)這種情況。
假設(shè),我們要開發(fā)一個數(shù)據(jù)庫訪問的包裝庫。為其它程序員提供方便快捷地訪問數(shù)據(jù)庫的能力,使他們免于和難纏的ODBC或OleDB打交道。但是,前提是我們的包裝庫不能像ADO.net那樣折損開發(fā)人員的訪問能力。
基于這種前提,我們需要考慮數(shù)據(jù)庫訪問的幾個基本要素。我歸納了一下,大概可以包括這么幾個:游標(biāo)、數(shù)據(jù)綁定、數(shù)據(jù)緩沖。為了簡化,其他細(xì)枝末節(jié)暫不考慮。同時,只考慮結(jié)果集處理部分。下面,我們將考察兩種不同的實(shí)現(xiàn)方式:OOP和GP。
先看OOP方式。OOP方式利用多態(tài)和后期綁定提供了擴(kuò)展能力和一致的接口。代碼大概會是這樣:
class MyDBAccessor
{

virtual bool MoveNext();
virtual bool MovePre();
virtual bool MoveFirst();
virtual bool MoveLast();

virtual bool GetData(const string& field, void** data);
virtual bool GetData(int field, void** data);
virtual bool SetData(const string& field, void* data) {…}
virtual bool SetData(int field, void* data) {…}

virtual void BindColumn(const string& field, DBType type);
virtual void BindColumn(int field, DBType type);

private:
virtual void DefaultBind()=0;

};
因?yàn)檫@里只關(guān)心程序的結(jié)構(gòu),所以只給出聲明,略去定義。MyDBAccessor定義了一個基本的框架,對于不同的特性支持,比如不同的游標(biāo)等等,通過在繼承類中重載相應(yīng)的虛函數(shù)實(shí)現(xiàn):
class MyDBFFAccessor//Fast-forward游標(biāo)類型
: public MyDBAccessor
{

virtual bool MoveNext(){…}
virtual bool MovePre(){…}
virtual bool MoveFirst(){…}
virtual bool MoveLast(){…}

};
那么,當(dāng)我們需要一個支持Fast-forward游標(biāo),自動綁定,并且按行緩沖的數(shù)據(jù)庫訪問類時,我們定義了如下的類:
class MyDB_FF_Dyn_Row
: public MyDBAccessor
{

virtual bool MoveNext(){…}
virtual bool MovePre(){…}
virtual bool MoveFirst(){…}
virtual bool MoveLast(){…}

virtual bool GetData(const string& field, void** data) {…}
virtual bool GetData(int field, void** data) {…}
virtual bool SetData(const string& field, void* data) {…}
virtual bool SetData(int field, void* data) {…}

private:
virtual void DefaultBind(){…}

};
如果我們需要一個支持Dynamic游標(biāo),字符串綁定(所有類型轉(zhuǎn)換成字符串),塊緩沖的數(shù)據(jù)庫訪問類,那么就再定義一個繼承類。
問題來了,游標(biāo)類型至少有8種,假設(shè)默認(rèn)綁定方式有5種(自動、寬/窄字符串綁定、xml綁定、手工綁定),數(shù)據(jù)緩沖方式有3種(行緩沖、塊緩沖、數(shù)組緩沖)。
那么我們得定義多少個繼承類呢?8*5*3+1=121。Mission Impossible,除非你有ms那樣的資源。
現(xiàn)在,我們來看看GP(范型編程)方式會不會好一些。我們先定義一個類模板:
template<class Cursor, class Binder, class RowBuffer>
class MyDBAccessor
: public Cursor, public Binder, public RowBuffer
{

};
應(yīng)該看出來了吧,模板MyDBAccessor繼承自模板類型參數(shù)Cursor、Binder、RowBuffer。而這三個模板參數(shù)分別對應(yīng)了游標(biāo)管理類、綁定類和行緩沖類。根據(jù)前面的假設(shè),我們定義了8種游標(biāo)管理類:
class FastForwardCursor
{
public:
bool MoveNext();
bool MoveLast();
bool GetData(const string& field, void** data);
bool GetData(int field, void** data);
bool SetData(const string& field, void* data) {…}
bool SetData(int field, void* data) {…}
};
class FastForwardReadOnlyCursor
{
public:
bool MoveNext();
bool MoveLast();
bool GetData(const string& field, void** data);
bool GetData(int field, void** data);
};

class DynamicCursor
{
public;
bool MoveNext();
bool MovePre();
bool MoveLast();
bool MoveFirst();
bool GetData(const string& field, void** data);
bool GetData(int field, void** data);
bool SetData(const string& field, void* data) {…}
bool SetData(int field, void* data) {…}
};
細(xì)心的人會發(fā)現(xiàn)這些游標(biāo)管理類的接口(成員聲明)都不一樣,一會兒會告訴你為什么。
其他的綁定類和數(shù)據(jù)緩沖類都依次定義。當(dāng)我們需要一個支持Fast-forward游標(biāo),自動綁定,并且按行緩沖的數(shù)據(jù)庫訪問類時,只需用相應(yīng)的類實(shí)例化模板即可:
MyDBAccessor<FastForwardCursor, DynamicBinder, SingleRowBuffer>da;

如果我們需要一個支持Dynamic游標(biāo),字符串綁定(所有類型轉(zhuǎn)換成字符串),快緩沖的數(shù)據(jù)庫訪問類,也很方便:
MyDBAccessor<DynamicCursor, StringBinder, BulkBuffer>da;

在GP方式中,我們只需定義8+5+3+1=17個類和模板,即可實(shí)現(xiàn)OOP方式中121類定義才能達(dá)到的效果。
非常好吧。還不止于此。假設(shè)你希望用DynamicCursor游標(biāo)訪問數(shù)據(jù)庫,但是寫錯了變成了這樣:
MyDBAccessor<FastForwardCursor, DynamicBinder, SingleRowBuffer>da;

da.MoveFirst();//啊呀!
不要告訴我你不會犯這種低級錯誤。這種錯誤每時每刻都在發(fā)生,最可能的一種情況就是軟件的設(shè)計改了,由fast-forward游標(biāo)改成dynamic游標(biāo),而你卻忘了修改da的聲明。
此 時,代碼不會編譯通過。因?yàn)镸yDBAccessor繼承自游標(biāo)管理類,并從游標(biāo)管理類繼承了操縱游標(biāo)的成員函數(shù)。于是,根據(jù)fast-forward的 定義:只進(jìn)不退,所以沒有MoveFirst()函數(shù)(也不需要,對吧)。因此,MyDBAccessor<FastForwardCursor, DynamicBinder, SingleRowBuffer>也沒有這個函數(shù)。那么da.MoveFirst()便會引發(fā)編譯錯誤。
很 多初學(xué)者可能不喜歡這種設(shè)計,因?yàn)樗麄兎浅:ε戮幾g器錯誤。就好像編譯器是他們中學(xué)語文老師一樣。其實(shí),你應(yīng)該感謝這個編譯錯誤,它在第一時間幫你消除了 一個潛在的bug。如果我們在FastForwardCursor中加上MoveFirst()這個函數(shù),編譯自然沒有問題。但在運(yùn)行時,這句代碼肯定會 引發(fā)一個運(yùn)行時錯誤。運(yùn)氣好的話在你測試的時候,運(yùn)氣不好的話可能會在你客戶心情最差的時候發(fā)生。這個后果,…,哎呀呀。
另外,即使在你測試的時候發(fā)生,你也會被迫用幾十上百個單步追查問題的原因,以至于把你周末約會女朋友的心情都搞壞了。
好 了,乘法變加法的把戲變完了。簡單地講,就是你可以利用模板、繼承模板參數(shù),以及多繼承等技術(shù),將一些基本的要素組合起來,構(gòu)成一個復(fù)雜的,功能完整的 類。用最少的代碼作最多的事。這種技術(shù)是由C++領(lǐng)域的先鋒官Andrei Alexandresu提出來的,稱為policy。更詳細(xì)的內(nèi)容,可以參考他的《Modren C++ Design》,里面有很詳細(xì)的講解和案例。不過得做好心理準(zhǔn)備,接受大劑量模板。

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多