• <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>

            longshanks

              C++博客 :: 首頁(yè) :: 聯(lián)系 :: 聚合  :: 管理
              14 Posts :: 0 Stories :: 214 Comments :: 0 Trackbacks

            常用鏈接

            留言簿(10)

            我參與的團(tuán)隊(duì)

            搜索

            •  

            最新評(píng)論

            閱讀排行榜

            評(píng)論排行榜

            業(yè)務(wù)邏輯中,很多邏輯上不同類(lèi)型的東西,到了編程語(yǔ)言中,時(shí)常會(huì)退化成一種類(lèi)型。一個(gè)最簡(jiǎn)單的例子就是貨幣。通常在我們編程時(shí),采用一種類(lèi)型,如double(有些系統(tǒng)中有專(zhuān)門(mén)的Currency類(lèi)型,為了簡(jiǎn)便起見(jiàn),這里使用double),來(lái)表示貨幣。
            但是,隨之而來(lái)的就是幣種的問(wèn)題。不同的幣種間存在換算,也就是匯率的問(wèn)題。比如我們有RMB和USD兩種貨幣,分別表示人民幣和美元。盡管都是貨幣(在代碼中有相同的類(lèi)型),我們卻不能對(duì)他們直接賦值:
            double rmb_;
            double usd_=100;
            rmb_=usd_;        //絕對(duì)不行,100美元可相當(dāng)于768元人民幣,盡管人民幣在升值
            必須進(jìn)行匯率換算:
            rmb_=usd_*e_rate;
            e_rate就是匯率。這個(gè)誰(shuí)都清楚。在邏輯上,100美元和768元人民幣是等價(jià)的(假設(shè)今天的匯率是7.68),是可以?xún)稉Q的。但在軟件中,我們不能簡(jiǎn)單的賦值了事,必須做換算。
            現(xiàn)在我們希望用代碼直接表現(xiàn)邏輯上的意義,也就是用賦值操作:=,實(shí)現(xiàn)貨幣間的換算,該怎么做呢?啊對(duì),沒(méi)錯(cuò),操作符重載。
            我們可以重載operator=操作符,使其具備匯率換算的功能。(或許有人會(huì)提出異議,改變一個(gè)操作符已有的語(yǔ)義,是否違背大師們的教誨。但我個(gè)人認(rèn)為,語(yǔ)義應(yīng)當(dāng)遵從業(yè)務(wù)邏輯,既然按照邏輯含義進(jìn)行重載,不應(yīng)該引發(fā)什么糾紛。否則還需要重載干嗎?)但問(wèn)題是,重載依賴(lài)于不同的類(lèi)型,double operator=(double)的操作符定義是默認(rèn)的,已經(jīng)存在,無(wú)法以相同形式重載。再說(shuō),即便是可以,復(fù)制對(duì)象和被賦值對(duì)象的類(lèi)型相同,如何區(qū)分兩種類(lèi)型的轉(zhuǎn)換呢?
            很明顯,我們需要新的類(lèi)型。typedef肯定是沒(méi)指望的,因?yàn)樗鼉H僅為一個(gè)類(lèi)型起別名,并沒(méi)有產(chǎn)生新的類(lèi)型。所以,我們只能求助于類(lèi)。我們可以以如下方式定義各種不同的貨幣類(lèi):
            class RMB
            {
            public:
                double    _val;
            };
            class USD
            {
            public:
                double    _val;
            };

            這樣,便可以針對(duì)不同貨幣重載operator=:
            class RMB
            {
            public:
                RMB operator=(const RMB& v) {
                    _val=v._val;
                }
                RMB operator=(const USD& v) {
                    _val=v._val*e_rate;    //貨幣換算
                }
            public:
                double    _val;
            };
            class USD
            {
            public:
                USD operator=(const USD& v) {
                    _val=v._val;
                }
                USD operator=(const RMB & v) {
                    _val=v._val/e_rate;    //貨幣換算
                }
            public:
                double    _val;
            };
            這樣,我們便可以對(duì)兩種貨幣賦值了:
            RMB    rmb_;
            USD    usd_;
            rmb_=usd_;        //帶貨幣換算的賦值操作
            根據(jù)這個(gè)方法,我們一直往下推,可以構(gòu)造出各種各樣的貨幣,并且定義它們之間的轉(zhuǎn)換:
            class UKP    //英鎊
            {…}
            class JPD    //日元
            {…}

            不過(guò)有個(gè)問(wèn)題,如果有10中貨幣,我們必須定義100個(gè)operator=的重載,而且都是些重復(fù)代碼。這樣太蠢了。得采用更好的方法才能實(shí)現(xiàn)我們的理想。
            注意觀(guān)察,每個(gè)貨幣類(lèi)的代碼都符合同一種模式,有很強(qiáng)的規(guī)律性。看出來(lái)了吧,這種情況非常適合使用C++的超級(jí)武器——模板。沒(méi)錯(cuò),說(shuō)做就做:
            template
            <int CurrType>
            class Currency
            {
            public:
                double    _val;
            };
            注意看,這里非常規(guī)地使用了模板的一個(gè)特性:非類(lèi)型模板參數(shù),就是那個(gè)int CurrType。模板參數(shù)通常都是一個(gè)類(lèi)型,比如int什么的。但也可以是一個(gè)非類(lèi)型的模板參數(shù),就象這里的CurrType。傳統(tǒng)上,非類(lèi)型模板參數(shù)用于傳遞一個(gè)靜態(tài)的值,用來(lái)構(gòu)造模板類(lèi)。但在這里,這個(gè)模板參數(shù)并沒(méi)有被模板使用,也永遠(yuǎn)不會(huì)被使用。這個(gè)模板參數(shù)的作用就是“制造類(lèi)型”:
            typedef    Currency
            <0>    RMB;    //人民幣
            typedef    Currency
            <1>    USD;    //美元
            typedef    Currency
            <2>    UKP;    //英鎊
            typedef    Currency
            <3>    JPD;    //日元

            typedef本身不會(huì)產(chǎn)生新的類(lèi)型,但是這里Currency
            <n>已經(jīng)是完全不同的類(lèi)型了。當(dāng)一個(gè)模板被實(shí)例化成一個(gè)類(lèi)的時(shí)候,只要模板參數(shù)的實(shí)參有所不同,便是一個(gè)不同的類(lèi)型。我們利用了模板的這個(gè)特性,憑空制造出任意多個(gè)結(jié)構(gòu)完全相同,但卻是完全獨(dú)立的類(lèi)型。
            好,下一步,便是重載operator=操作符。當(dāng)然不能再做為每一對(duì)貨幣類(lèi)型重載operator=的蠢事了。用一個(gè)成員函數(shù)模板就可以解決問(wèn)題:
            double e_rate[10][10];        //匯率表

            template
            <int CurrType>
            class Currency
            {
            public:
                template
            <int ct2>
                Currency
            <CurrType>& operator=(count Currency<ct2>& v) {
                    _val=v._val * e_rate[ct2][CurrType];    //找出匯率表中相應(yīng)的匯率,
                                                            // 計(jì)算并賦值
                }
            public:
                double    _val;
            };
            操作符operator=的代碼中,賦值對(duì)象v的值乘上一個(gè)匯率,這個(gè)匯率存放在匯率表中,通過(guò)模板參數(shù)CurrType和ct2檢索(當(dāng)然匯率表得足夠大)。
            這樣,我們便可以隨意地賦值,而無(wú)須關(guān)心貨幣轉(zhuǎn)換的問(wèn)題了:
            ///初始化匯率表
            e_rate[0][0]=1;
            e_rate[1][0]=7.68;

            //使用貨幣
            USD    usd_;
            UKP    ukp_;
            JPD    jpd_;

            jpd_=usd_=ukp=rmb_;    //成功!一切順心。
            需要說(shuō)明的是,匯率表并沒(méi)有在聲明時(shí)就初始化,是考慮到匯率經(jīng)常變動(dòng),不應(yīng)當(dāng)作為常量寫(xiě)死在代碼中。更進(jìn)一步可以使用一個(gè)類(lèi)封裝成可變大小的匯率表,甚至可以用某個(gè)文件或數(shù)據(jù)庫(kù)對(duì)其初始化。
            問(wèn)題當(dāng)然還有,貨幣是要參與運(yùn)算的,否則沒(méi)有什么用處。所以,我們還得使這些貨幣具備基本的計(jì)算能力。貨幣的計(jì)算,根據(jù)業(yè)務(wù)邏輯大致應(yīng)具備以下能力:
            1.    +、-:兩種貨幣的加法和減法,允許不同種貨幣參與計(jì)算,必須考慮轉(zhuǎn)換操作,返回做操作數(shù)類(lèi)型;
            2.    *、/:貨幣乘上或除以一個(gè)標(biāo)量值,這里設(shè)定為double。但兩種貨幣不能相乘或相除。
            3.    ==、!=:比較兩種貨幣,允許不同種貨幣參與比較,但必須考慮轉(zhuǎn)換操作。
            還有其他的操作,暫不做考慮,畢竟這里的目的不是開(kāi)發(fā)一個(gè)完整的貨幣系統(tǒng)。為了編碼上的方便,這里同時(shí)還定義了四則運(yùn)算的賦值版本:+=、-=、*=、/=。為了節(jié)省篇幅,這里只展示+、*和==的代碼,其他代碼類(lèi)推:
            template
            <int ty, int tp>
            inline bool operator==(currency
            <ty>& c1, const currency<tp>& c2) {
                return    c1._val==c2._val*curr_rate[tp][ty];
            }

            template
            <int ty, int tp>
            inline currency
            <ty>& operator+=(currency<ty>& c1, const currency<tp>& c2) {
                c1._val+=c2._val*curr_rate[tp][ty];
                return    c1;
            }
            template
            <int ty, int tp>
            inline currency
            <ty> operator+(currency<ty>& c1, const currency<tp>& c2) {
                currency
            <ty> t(c1);
                t+=c2;
                return    t;
            }
            請(qǐng)注意==和+操作符中的的貨幣轉(zhuǎn)換運(yùn)算,每次都是將第二操作數(shù)貨幣轉(zhuǎn)換成第一操作數(shù)貨幣后再進(jìn)行運(yùn)算操作。第一參數(shù)和第二參數(shù)的類(lèi)型不同,因此允許不同貨幣進(jìn)行計(jì)算。這可以進(jìn)一步簡(jiǎn)化代碼,完全以邏輯的方式編程。
            template
            <int ty>
            inline currency
            <ty>& operator*=(currency<ty>& c1, const double q) {
                c1._val*=q;
                return    c1;
            }
            template
            <int ty>
            inline currency
            <ty> operator*(currency<ty>& c1, const double q) {
                currency
            <T, ty>    t(c1);
                t*=q;
                return    t;
            }

            template
            <int ty>
            inline currency
            <ty>& operator*=(const double q,currency<ty>& c1) {
                return    operator*=(c1, q);
            }
            template
            <int ty>
            inline currency
            <ty> operator*(const double q,currency<ty>& c1) {
                return    operator*(c1, q);
            }

            *操作符的參數(shù)只有一個(gè)是貨幣類(lèi)型,另一個(gè)是double類(lèi)型,表示數(shù)量。只有貨幣乘上數(shù)量才有意義,不是嗎?*操作符包括兩個(gè)版本,一個(gè)貨幣在前,數(shù)量在后;另一個(gè)數(shù)量在前,貨幣在后。為的是適應(yīng)rmb_*1.4和1.4*rmb_兩種不同的寫(xiě)法,算法是完全一樣的。
            現(xiàn)在,貨幣可以運(yùn)算了:
            usd_=usd_*3;    //同種貨幣運(yùn)算
            ukp_=rmb_*2.5;        ///計(jì)算後直接賦值給另一種貨幣
            jpd_=ukp_=rmb_+usd_;    ///同上,但有四種貨幣參與運(yùn)算
            現(xiàn)在,貨幣運(yùn)算非常方便了,不需要考慮貨幣種類(lèi),貨幣的轉(zhuǎn)換是自動(dòng)的,無(wú)需額外代碼。
            在簡(jiǎn)化代碼的同時(shí),也提供了操作上的約束,比如:
            ukp_=rmb_*usd_;    ///編譯錯(cuò)誤。貨幣乘上另一種貨幣無(wú)意義!!!
            這句代碼會(huì)引發(fā)編譯錯(cuò)誤,因?yàn)槲覀儧](méi)有為兩種貨幣相乘提供*的重載。很明顯,一種貨幣與另一種貨幣相乘是根本沒(méi)有意義的。這里通過(guò)靜態(tài)的重載類(lèi)型檢查,對(duì)施加在貨幣上的運(yùn)算做出約束。促使違背邏輯的代碼在第一時(shí)間被攔截,避免出現(xiàn)運(yùn)行時(shí)錯(cuò)誤。要知道,兩種貨幣相乘,賦給另一個(gè)貨幣的錯(cuò)誤是非常隱蔽的,只有盤(pán)庫(kù)或結(jié)賬的時(shí)候才會(huì)發(fā)現(xiàn)。
            很好,這里我們利用了C++模板的一些特殊機(jī)制,以及操作符模板、操作符重載等技術(shù),開(kāi)發(fā)一個(gè)貨幣系統(tǒng)。這個(gè)系統(tǒng)可以用最簡(jiǎn)潔的語(yǔ)句實(shí)現(xiàn)各種貨幣的計(jì)算和轉(zhuǎn)換功能。同時(shí),還利用重載機(jī)制的強(qiáng)類(lèi)型特性,提供了符合業(yè)務(wù)邏輯的操作約束。
            貨幣運(yùn)算只是一個(gè)簡(jiǎn)單的案例,但相關(guān)的技術(shù)可以進(jìn)一步推廣到更復(fù)雜的領(lǐng)域中。而且業(yè)務(wù)越復(fù)雜,所得到的收益越多。因此,充分理解并運(yùn)用C++所帶來(lái)的泛型編程功能,可以大大簡(jiǎn)化軟件的開(kāi)發(fā)、減少代碼的錯(cuò)誤,降低開(kāi)發(fā)的成本。
            這種技術(shù)適合用在一些邏輯上存在差異,但在物理上具備相同特征的實(shí)體上。一方面使這些實(shí)體在代碼中強(qiáng)類(lèi)型化,以獲得重載和類(lèi)型檢測(cè)能力。由于代碼中邏輯實(shí)體的對(duì)應(yīng)類(lèi)型強(qiáng)類(lèi)型化,是我們可以通過(guò)重載和靜態(tài)類(lèi)型檢測(cè)等技術(shù)手段,實(shí)現(xiàn)僅使用語(yǔ)言提供的要素,在代碼中直接構(gòu)造業(yè)務(wù)模型的能力。但手工對(duì)每一個(gè)邏輯實(shí)體進(jìn)行強(qiáng)類(lèi)型化,是費(fèi)力的和瑣碎的,并且存在著大量的重復(fù)勞動(dòng)。此時(shí),我們可以利用模板的抽象能力,反過(guò)來(lái)利用邏輯實(shí)體在物理上的共同特性,一次性構(gòu)建抽象的模板,并利用模板實(shí)例化的一些特性,很方便地構(gòu)造新的類(lèi)型(僅僅一個(gè)typedef)。
            這種技術(shù)進(jìn)一步擴(kuò)展后,可以有更高級(jí)的應(yīng)用。一個(gè)經(jīng)典的范例就是實(shí)現(xiàn)編譯期的量綱分析。在Template Meta-programming一書(shū)中,對(duì)此有詳細(xì)的講解。

            作為一個(gè)好事者,我希望能夠給我周邊的人講解這種技術(shù)。他們對(duì)C++很不熟悉,但熟悉C#。于是,我打算把這種技術(shù)移植到C#中,以便於講解。說(shuō)做就做。
            我建了一個(gè)C#項(xiàng)目,把代碼拷貝過(guò)去,然后著手修改,這樣可以省些事。我立刻便遇到了問(wèn)題。C#有泛型,相當(dāng)于模板,但不支持非類(lèi)型泛型參數(shù),即int CurrType,只允許用一個(gè)類(lèi)型作為泛型參數(shù)。這樣我們就不能使用C++中耍的手法了(typedef currency
            <n>)。退而求其次,直接用類(lèi)實(shí)現(xiàn)貨幣類(lèi)型:
            class RMB
            {
            public double _val;
            }
            class USD
            {
            public double _val;
            }

            這樣太繁瑣了,很多重復(fù)。我們可以用一個(gè)基類(lèi)封裝_val,貨幣類(lèi)從基類(lèi)上繼承獲得:
            class CurrBase
            {
            public double _val;
            }
            class RMB : CurrBase
            {
            }
            class USD : CurrBase
            {
            }

            貨幣類(lèi)都是空的,它們的存在只是為了創(chuàng)建一個(gè)新的類(lèi)型。
            現(xiàn)在處理貨幣轉(zhuǎn)換問(wèn)題。C#不能重載operator=,所以只能使用一個(gè)helper函數(shù)泛型asign代替:
            class utility
            {
                 public static void asign
            <T1, T2>(T1 c1, T2 c2)
                     where T1 : CurrBase
                     where T2 : CurrBase
                 {
                     c1._val = c2._val * utility.e_rate[c2.CurID(),c1.CurID()];
                 }
            }
            這個(gè)asign函數(shù)是個(gè)泛型,泛型參數(shù)分別代表了兩個(gè)操作數(shù),函數(shù)中執(zhí)行了貨幣轉(zhuǎn)換。為了能夠在匯率表中檢索到相應(yīng)的匯率,我們必須為基類(lèi)和貨幣類(lèi)定義抽象函數(shù):
                public abstract class CurrBase
                {
                    public double _val=0;
                    public abstract int CurID();
                }
                public class RMB : CurrBase
                {
                    public override int CurID()
                    {
                        return 0;
                    }
            }

            基類(lèi)中聲明了CurID()抽象方法,并在貨幣類(lèi)中定義。這樣,便可以用統(tǒng)一的方式進(jìn)行貨幣轉(zhuǎn)換了:
            asign(rmb_, usd_);
            還行,盡管不那么漂亮,但也還算實(shí)用。不過(guò),當(dāng)我多看了幾眼代碼后,便發(fā)現(xiàn)這里根本沒(méi)有必要使用泛型。完全可以利用OOP的多態(tài)實(shí)現(xiàn)同樣的功能:
                 public static void asign(CurrBase c1, CurrBase c2)
                 {
                     c1._val = c2._val * utility.e_rate[c2.CurID(),c1.CurID()];
                 }
            不過(guò)也沒(méi)關(guān)系,使用方式還沒(méi)有變,代碼反而更簡(jiǎn)單了。使用泛型畢竟不是我們的根本目的,對(duì)吧?
            現(xiàn)在輪到運(yùn)算符了。不過(guò)我不知該把泛型運(yùn)算符定義在哪里。按C#文檔里的要求,運(yùn)算符必須是類(lèi)的static成員。但我的泛型運(yùn)算符是針對(duì)許多個(gè)貨幣類(lèi)的,定義在任何一個(gè)中,對(duì)其他類(lèi)似乎不太公平。于是,我決定嘗試將其定義在基類(lèi)里:
                public abstract class CurrBase
            {
                …
                    public static CurrBase operator+
            <T1, T2>(T1 c1, T2 c2)
                        where T1 : CurrBase
                        where T2 : CurrBase
                    {
                        …
                    }
            }
            編譯器立刻還我以顏色:操作符根本不能是泛型!好吧,不能就不能吧,繼續(xù)退而求其次,用OOP:
                public abstract class CurrBase
            {
                …
                    public static CurrBase operator+(CurrBase c1, CurrBase c2)
                    {
                        …
                    }
            }
            不過(guò),這次讓步讓得有點(diǎn)離譜。當(dāng)我寫(xiě)下這樣的代碼時(shí),編譯器居然不認(rèn)賬:
            rmb_=rmb_+usd_;
            錯(cuò)誤信息是:錯(cuò)誤 CS0266: 無(wú)法將類(lèi)型“st_in_cs.CurrBase”隱式轉(zhuǎn)換為“st_in_cs.RMB”。存在一個(gè)顯式轉(zhuǎn)換(是否缺少?gòu)?qiáng)制轉(zhuǎn)換?)。
            我非得采用強(qiáng)制類(lèi)型轉(zhuǎn)換,才能過(guò)關(guān):
            rmb_=(RMB)(rmb_+usd_);
            太夸張了,這樣肯定不行。于是,我被迫在每個(gè)貨幣類(lèi)中定義operator+:
            class RMB : CurrBase
            {

                public RMB operator+(RMB c1, USD c2)
                {
                    …
                }
                public RMB operator+(RMB c1, UKP c2)
                {
                    …
                }

            }
            這可不得了,我必須為每對(duì)貨幣類(lèi)定義一個(gè)+操作符,+操作符的總數(shù)將會(huì)是貨幣類(lèi)數(shù)量的平方!其他的操作符每個(gè)都是貨幣類(lèi)數(shù)的平方。我可受不了!
            好在,可愛(ài)的OOP為我們提供了一根稻草,使得每個(gè)貨幣類(lèi)的每個(gè)操作符只需定義一個(gè):
            class RMB : CurrBase
            {

                public RMB operator+(RMB c1, CurrBase c2)
                {
                    …
                }

            }
            這樣,任何貨幣類(lèi)都可以作為第二操作數(shù)參與運(yùn)算,而操作符只需定義一個(gè)。這樣的工作量,對(duì)于一個(gè)逆來(lái)順受的程序員而言,還是可以接受的。很好,代碼不出錯(cuò)了:
            rmb_=rmb_+usd_;
            但當(dāng)我寫(xiě)下如下代碼時(shí),編譯器又開(kāi)始抱怨了:
            ukp_ = rmb_ + usd_;
            還是要求顯示轉(zhuǎn)換,除非我們?yōu)閁KP定義隱式類(lèi)型轉(zhuǎn)換操作符:
            class UKP
            {

                public static implicit operator UKP(RMB v)
                {
                    …
                }

            }
            光有RMB的不行啊,還得有USD的、JPD…。不過(guò)這樣的話(huà),我們必須為每一個(gè)貨幣類(lèi)定義所有其它貨幣類(lèi)的類(lèi)型轉(zhuǎn)換操作符。又是一個(gè)組合爆炸。到這里,我已經(jīng)黔驢技窮了。誰(shuí)讓C#不支持=操作符重載和操作符模板化呢。沒(méi)辦法,只能忍著點(diǎn)了。
            不過(guò),如果我們能夠降低點(diǎn)要求,事情還是有轉(zhuǎn)機(jī)的。如果我們不通過(guò)操作符,而是采用static成員方法,進(jìn)行貨幣的運(yùn)算的話(huà),就可以省去很多代碼了:
                public class utility
                {
                    public static T1 asign
            <T1, T2>(T1 c1, T2 c2)
                        where T1 : CurrBase, new()
                        where T2 : CurrBase
                    {
                        c1._val = c2._val * utility.curr_rate[c2.CurID(),c1.CurID()];
                        return    c1;
                    }
                    public static T1 add
            <T1, T2>(T1 c1, T2 c2)
                        where T1 : CurrBase, new()
                        where T2 : CurrBase
                    {
                        T1 t=new T1();
                        t._val=c1._val + c2._val * 
                            utility.curr_rate[c2.CurID(),c1.CurID()];
                        return t;
                    }
                    …
            }
            這里,我還是使用了泛型,因?yàn)檫@些函數(shù)需要返回一個(gè)值,只有使用泛型,才能返回一個(gè)明確的類(lèi)型,以避免強(qiáng)制轉(zhuǎn)換的要求。于是,賦值和計(jì)算的代碼就成了:
            asign(jpd_, asign(ukp_, add(rmb_, usd_)));//也就是jpd_=ukp_=rmb_+usd_
            的確是難看了點(diǎn),但是為了能夠少寫(xiě)點(diǎn)代碼,這也只能將就了。
            好了,我盡了最大的努力,試圖在C#中實(shí)現(xiàn)強(qiáng)類(lèi)型、可計(jì)算的貨幣系統(tǒng)。盡管最終我可以在C#中開(kāi)發(fā)出一組與C++具有相同效果的貨幣類(lèi)(除了賦值操作以外),但需要我編寫(xiě)大量的代碼,實(shí)現(xiàn)各種計(jì)算操作,以及貨幣類(lèi)之間的類(lèi)型轉(zhuǎn)換操作(組合爆炸)。相比C++中總共200來(lái)行代碼,的確復(fù)雜、臃腫得多。
            我并不希望把這篇文章寫(xiě)成“C++ vs C#”,(盡管我很高興看到C++比C#強(qiáng)?)。我希望通過(guò)對(duì)這樣一個(gè)代碼優(yōu)化任務(wù),顯示不同技術(shù)運(yùn)用產(chǎn)生的結(jié)果。同時(shí),也可以通過(guò)這兩種實(shí)現(xiàn)嘗試的對(duì)比,了解泛型編程的作用,以及泛型編程對(duì)語(yǔ)言提出的要求。
            毋庸置疑,C++采用了純粹的泛型編程,因此可以對(duì)問(wèn)題進(jìn)行高度抽象。并利用問(wèn)題所提供的每一點(diǎn)有助于抽象的信息,以最簡(jiǎn)的形式對(duì)問(wèn)題建模。而作為以O(shè)OP為核心的語(yǔ)言C#,對(duì)泛型的支持很弱。更重要的是,C#的泛型對(duì)泛型參數(shù)的運(yùn)用嚴(yán)重依賴(lài)於泛型參數(shù)的約束(where)。如果沒(méi)有where,C#將泛型參數(shù)作為Object類(lèi)型處理,此時(shí)泛型參數(shù)沒(méi)有意義(我無(wú)法訪(fǎng)問(wèn)該類(lèi)型的成員)。如果有了where,C#要求泛型參數(shù)必須同where中指定的類(lèi)型有繼承關(guān)系(如asign中的T1必須是CurrBase的繼承類(lèi))。而泛型函數(shù)中對(duì)泛型參數(shù)的使用也局限在約束類(lèi)型(即CurrBase)上。于是,我們可以直接用以基類(lèi)(CurrBase)為參數(shù)的asign函數(shù)代替泛型版本的asign。由于C#對(duì)泛型參數(shù)的繼承性要求,使得泛型被困住了手腳,無(wú)法發(fā)揮應(yīng)用的作用。正由于這些問(wèn)題,C++才采用了現(xiàn)在模板的形式,而沒(méi)有采用同C#一樣的泛型模式。
            或許有人會(huì)說(shuō),既然OOP能解決問(wèn)題(asign最初不需要泛型也行,但最終還需要泛型來(lái)控制返回值的類(lèi)型),為什么還要GP呢?
            對(duì)于這個(gè)問(wèn)題,前面也給出了答案。由于C#的泛型不支持非類(lèi)型泛型參數(shù),因此迫使我們使用傳統(tǒng)OOP的手段:利用基類(lèi)實(shí)現(xiàn)貨幣類(lèi)的實(shí)現(xiàn),定義貨幣類(lèi)來(lái)創(chuàng)建新類(lèi)型,使貨幣強(qiáng)類(lèi)型化,利用虛函數(shù)提供貨幣獨(dú)有信息。僅這一層面,OOP方式已經(jīng)大大不如GP方式了,GP僅定義了一個(gè)模板,所有的貨幣類(lèi)型都是通過(guò)typedef一句語(yǔ)句產(chǎn)生,無(wú)需更多的代碼。而OOP方式要求必須為每一個(gè)貨幣編寫(xiě)一個(gè)類(lèi),代碼量明顯多于GP方式。
            此后,C++通過(guò)重載一組操作符模板,實(shí)現(xiàn)貨幣的運(yùn)算。而貨幣模板以及生成貨幣類(lèi)型的typedef都無(wú)須任何改變。而在C#中,由于不支持泛型操作符,被迫定義大量的特定于類(lèi)型的操作符。所有運(yùn)算操作符,在每個(gè)貨幣類(lèi)中都必須重載一次。而轉(zhuǎn)型操作符,則必須在每個(gè)貨幣類(lèi)中重載n-1次。
            換句話(huà)說(shuō),有n種貨幣,有m個(gè)操作符(包括轉(zhuǎn)型操作符),那么就需要定義n+1個(gè)類(lèi)(包括基類(lèi)),n×m+n×(n-1)個(gè)操作符。假設(shè)n=10,m=10,那么總共需要11個(gè)類(lèi)定義,190個(gè)操作符重載!如果每個(gè)類(lèi)定義需要20行代碼,而每個(gè)操作符重載需要5行代碼,那么總共需要1170行代碼。如果貨幣數(shù)量增加,總的代碼數(shù)將以幾何級(jí)數(shù)的方式增長(zhǎng)。
            上面的計(jì)算表明,盡管OOP可以解決問(wèn)題,實(shí)現(xiàn)我們的目標(biāo),但所帶來(lái)的開(kāi)發(fā)量和維護(hù)量卻是難以承受的。而且,OOP的方式擴(kuò)展非常困難,隨著系統(tǒng)規(guī)模的擴(kuò)大,擴(kuò)展將越來(lái)越困難。所有這些都表明一點(diǎn),盡管OOP是軟件工程的明星,但在實(shí)際情況下,很多地方存在著OOP無(wú)法解決或難以解決的問(wèn)題。這也就是為什么業(yè)界的先鋒人物不斷拓展和強(qiáng)化泛型編程的原因。
            posted on 2007-05-31 14:25 longshanks 閱讀(1467) 評(píng)論(6)  編輯 收藏 引用

            Feedback

            # re: 業(yè)務(wù)邏輯的強(qiáng)類(lèi)型化 2007-06-04 14:15 看圖軟件
            真是好東西  回復(fù)  更多評(píng)論
              

            # re: 業(yè)務(wù)邏輯的強(qiáng)類(lèi)型化 2007-06-06 23:30 yaotang
            從別人的轉(zhuǎn)載然后來(lái)到原作者這兒,呵呵,不錯(cuò)不錯(cuò),謝謝;期待更多精彩:-)  回復(fù)  更多評(píng)論
              

            # re: 業(yè)務(wù)邏輯的強(qiáng)類(lèi)型化 2007-11-08 20:46 TeirDal
            是否復(fù)雜化了?俺覺(jué)得,類(lèi)似python現(xiàn)在"每個(gè)類(lèi)型都是類(lèi)"的思想,把每個(gè)貨幣變成類(lèi),然后類(lèi)里面實(shí)現(xiàn)諸如RMB->toUSD()之類(lèi)方法的轉(zhuǎn)換就夠了。調(diào)用的時(shí)候簡(jiǎn)單,直觀(guān)...何必一定要隱式轉(zhuǎn)換?其實(shí)實(shí)現(xiàn)起來(lái)的時(shí)間更長(zhǎng)...  回復(fù)  更多評(píng)論
              

            # re: 業(yè)務(wù)邏輯的強(qiáng)類(lèi)型化 2007-11-09 08:49 longshanks
            to TeirDal:
            其實(shí),“每個(gè)類(lèi)型都是類(lèi)”和“每個(gè)類(lèi)都是類(lèi)型”是等價(jià)的,只要語(yǔ)言不再區(qū)分內(nèi)置類(lèi)型和用戶(hù)定義類(lèi)型這兩種概念。隨著操作符重載的出現(xiàn),我們有能力使得用戶(hù)定義類(lèi)型同內(nèi)置類(lèi)型具有相同的特性。
            貨幣是我能找到的最簡(jiǎn)單的案例,目的當(dāng)然不會(huì)是創(chuàng)建一個(gè)貨幣系統(tǒng)。主要是為了展示C++的gp和tmp的運(yùn)用。以及強(qiáng)類(lèi)型約束對(duì)于構(gòu)建穩(wěn)固、便捷的系統(tǒng)
            隱式轉(zhuǎn)換是利用=操作符實(shí)現(xiàn)轉(zhuǎn)換/賦值操作。它有一個(gè)好處就是:同一個(gè)語(yǔ)義,同一種形式。這種抽象的表達(dá)形式更貼近于現(xiàn)實(shí)業(yè)務(wù)。由于它在邏輯上的抽象性,那么對(duì)軟件的修改更加有利。比如:
            //成員函數(shù)
            USD u=20;
            RMB r;
            r=u->toUSD();
            //隱式轉(zhuǎn)換
            USD u=20;
            RMB r;
            r=u;
            如果r的類(lèi)型變成JPN,那么只需修改r的類(lèi)型即可,而無(wú)需做更多的改動(dòng)。
            //成員函數(shù)
            USD u=20;
            JPN r;
            r=u->toJPN();
            //隱式轉(zhuǎn)換
            USD u=20;
            JPN r;
            r=u;
            此外,多個(gè)香爐多個(gè)鬼,顯式的轉(zhuǎn)換函數(shù)可能寫(xiě)錯(cuò),會(huì)引發(fā)很多不必要的麻煩。
            實(shí)際上,使用成員函數(shù)的方案的工作量比我給出的方案來(lái)的大很多。因?yàn)槿绻衝個(gè)貨幣,那么每個(gè)貨幣類(lèi)都必須有n-1個(gè)轉(zhuǎn)換函數(shù)。所有轉(zhuǎn)換成員函數(shù)的總數(shù)將達(dá)到n*(n-1)。這是組合爆炸,也正是我的方案所要解決的問(wèn)題。
            如果你真要用顯式的轉(zhuǎn)換函數(shù),那么也應(yīng)該用成員函數(shù)模板:to<>()。這樣可以利用我在這里使用的元編程技巧把轉(zhuǎn)換函數(shù)的個(gè)數(shù)變成n。
            但在運(yùn)用了operator=()操作符模板后,只需要一個(gè)轉(zhuǎn)換函數(shù)即可。也就是說(shuō),這里的方案把原本n*(n-1)的代碼量變成了1。你說(shuō)實(shí)現(xiàn)起來(lái)哪個(gè)更長(zhǎng)?:)
            python的思想,我不能說(shuō)他錯(cuò),但用在這里并不合適,因?yàn)閜ython的應(yīng)用領(lǐng)域在于高層的應(yīng)用開(kāi)發(fā),而這里所探討的問(wèn)題則是基礎(chǔ)級(jí)別的軟件設(shè)計(jì)。python并不能應(yīng)付這里所面臨的問(wèn)題。
            這個(gè)貨幣系統(tǒng)的真正的問(wèn)題是,它是靜態(tài)的,而在現(xiàn)實(shí)軟件中,貨幣必須是動(dòng)態(tài)的,也就是說(shuō)我在編程的時(shí)候,并不知道我要把那兩種貨幣相加或者賦值。這樣,這個(gè)類(lèi)型系統(tǒng)也就失去作用了。  回復(fù)  更多評(píng)論
              

            # re: 業(yè)務(wù)邏輯的強(qiáng)類(lèi)型化 2007-11-09 17:25 江水獸
            非常希望今后能夠看到更多這樣的文章!  回復(fù)  更多評(píng)論
              

            # re: 業(yè)務(wù)邏輯的強(qiáng)類(lèi)型化 2008-02-18 17:00 dee
            建議看看這篇文章對(duì)你會(huì)有幫助的 http://www.150it.cn/bianchengwendang/Csap/5018299.html  回復(fù)  更多評(píng)論
              


            只有注冊(cè)用戶(hù)登錄后才能發(fā)表評(píng)論。
            網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問(wèn)   Chat2DB   管理


            一级做a爰片久久毛片16| 香蕉99久久国产综合精品宅男自| 久久精品国产72国产精福利| 热re99久久精品国99热| 久久午夜免费视频| 蜜桃麻豆www久久国产精品| 中文字幕亚洲综合久久| 久久99精品国产自在现线小黄鸭| 99久久国产精品免费一区二区 | 伊人久久精品无码av一区| 久久一区二区免费播放| 精品无码人妻久久久久久| 国产三级精品久久| 国产AV影片久久久久久| 久久99精品久久久久久9蜜桃| 91久久精品电影| 久久精品中文字幕有码| 久久久久黑人强伦姧人妻| 久久se这里只有精品| 久久久久国产一区二区| 色8激情欧美成人久久综合电| 久久久噜噜噜久久中文字幕色伊伊| 国内精品久久久久久久久| 久久人人爽人人爽人人片AV东京热| 亚洲乱码日产精品a级毛片久久| 久久亚洲国产成人精品无码区| 亚洲国产成人精品女人久久久 | 国产精品免费福利久久| 国产一区二区三区久久精品| 久久香蕉一级毛片| 久久久久99精品成人片| 久久精品国产乱子伦| 久久这里只有精品18| 亚洲午夜久久影院| 一本色道久久88综合日韩精品 | 青青草原综合久久大伊人| 久久精品亚洲日本波多野结衣| 91精品国产91久久综合| 国产AⅤ精品一区二区三区久久| 日韩美女18网站久久精品| 久久久精品人妻一区二区三区蜜桃|