• <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>
            隨筆 - 181  文章 - 15  trackbacks - 0
            <2008年12月>
            30123456
            78910111213
            14151617181920
            21222324252627
            28293031123
            45678910

            常用鏈接

            留言簿(1)

            隨筆分類

            隨筆檔案

            My Tech blog

            搜索

            •  

            最新評(píng)論

            閱讀排行榜

            評(píng)論排行榜

            代碼的壞味道
            3、Large Class(過(guò)大類)
            如果想利用單一class做太多事情,其內(nèi)往往就會(huì)出現(xiàn)太多instance變量。一旦如此,Duplicated Code也就接踵而至了。
            解決辦法:
            (1)Extract Class:將數(shù)個(gè)變量一起提煉至新class內(nèi),提煉時(shí)應(yīng)該選擇彼此相關(guān)的變量。
            當(dāng)某個(gè)class做了應(yīng)該由兩個(gè)class做的事的時(shí)候使用。
            建立一個(gè)新的class,將相關(guān)的值域和函數(shù)從舊class搬移到新class.
            步驟:
            1)決定如何分解class所負(fù)責(zé)任.
            2)建立一個(gè)新class,用以表現(xiàn)從舊class分離出來(lái)的責(zé)任.
            3)建立從舊class訪問新class的連接關(guān)系.
            4)對(duì)于你想搬移的每一個(gè)值域,用Move Field搬移之.
            5)每次搬移后,編譯,測(cè)試.
            6)使用Move Method將必要的函數(shù)搬移到新class,先搬移較低層次函數(shù),即被調(diào)用次數(shù)多余調(diào)用其他函數(shù)次數(shù)的函數(shù).再搬移較高層次的函數(shù).
            7)每次搬移之后,編譯,測(cè)試.
            8)檢查,精簡(jiǎn)每個(gè)class接口.
            9)決定是否讓新class曝光.

            為了加深對(duì)于重構(gòu)的理解,我開始尋找能夠?qū)嵺`這些方法的代碼.終于,我找到了大學(xué)時(shí)期寫的有關(guān)圖形處理的一個(gè)小程序,并打算對(duì)這個(gè)程序進(jìn)行重構(gòu).從某些方面講,它還是比較合適的:
            1、程序?qū)懙煤軤€,但是還能工作;
            2、包含了大部分書中提到的“壞味道”。
            3、有關(guān)位圖等圖形文件的格式信息和相關(guān)的規(guī)約還有算法,我已經(jīng)忘得差不多了,除了知道這些代碼是我自己寫得之外,我已經(jīng)基本上不認(rèn)識(shí)它們了。這樣我就可以實(shí)踐書中所說(shuō)的“邊重構(gòu),邊理解”的過(guò)程了。
            4、我寫這個(gè)程序的時(shí)候還比較勤快,寫了不少注釋。
            首先,我找到最長(zhǎng)的那個(gè)類,并繪制了類圖:

            這里面成員變量夠多。其中有一些只不過(guò)是聲明了一下;還有一些初始化了,但一次也沒有使用過(guò)。
            首先用這個(gè)類來(lái)實(shí)踐Extract Class的重構(gòu)方法。
            首先,我把其中有關(guān)直方圖的成員和方法獨(dú)立出來(lái),形成直方圖類,以變成下面這種形式:

            雖然很丑陋,但是還是先看一下在類CBasicBmpProcess中的InitHistoGram()方法:
            void CBasicBmpProcess::InitHistoGram()//初始化直方圖
            {
                
            long i;
                
            int j,BmpSize=bi.biWidth*bi.biHeight,ColorCount=bitCount<<8;
                
            if(ColorCount<=256)
                {
                    
            for(i=0;i<ColorCount;i++)
                    {
                        HistoGram[i]
            =0.0;
                        iHistoGram[i]
            =0;
                    }
                    
            for(i=0;i<BmpSize;i++)
                    {
                        iHistoGram[lpBuf[i]]
            ++;
                    }
                    
            for(i=0;i<ColorCount;i++)
                    {
                           HistoGram[i]
            =((float)iHistoGram[i])/BmpSize;
                    }
                }
                
            else
                {
                    
            for(i=0;i<ColorCount;i++)
                    {
                        HistoGramExt[i]
            =0.0;
                        iHistoGramExt[i]
            =0;
                    }
                    
            for(i=0;i<BmpSize-bitCount;i=i+bitCount)
                    {
                        
            for(j=0;j<bitCount;j++)
                        {
                            iHistoGramExt[j
            *ColorCount/bitCount+lpBuf[i+j]]++;
                        }
                    }
                    
            for(i=0;i<ColorCount;i++)
                    {
                           HistoGramExt[i]
            =10*((float)iHistoGramExt[i])/(BmpSize/bitCount);
                    }
                }

            }
            現(xiàn)在要把它抽取到新類CHistoGram中去。像下面這樣:
            void CHistoGram::InitHistoGram(int BmpSize,int ColorCount,BYTE *lpBuf,int bitCount)
            {
                
            long i=0,j=0;
                
            if(ColorCount<=256)
                {
                    
            for(i=0;i<ColorCount;i++)
                    {
                        HistoGram[i]
            =0.0;
                        iHistoGram[i]
            =0;
                    }
                    
            for(i=0;i<BmpSize;i++)
                    {
                        iHistoGram[lpBuf[i]]
            ++;
                    }
                    
            for(i=0;i<ColorCount;i++)
                    {
                           HistoGram[i]
            =((float)iHistoGram[i])/BmpSize;
                    }
                }
                
            else
                {
                    
            for(i=0;i<ColorCount;i++)
                    {
                        HistoGramExt[i]
            =0.0;
                        iHistoGramExt[i]
            =0;
                    }
                    
            for(i=0;i<BmpSize-bitCount;i=i+bitCount)
                    {
                        
            for(j=0;j<bitCount;j++)
                        {
                            iHistoGramExt[j
            *ColorCount/bitCount+lpBuf[i+j]]++;
                        }
                    }
                    
            for(i=0;i<ColorCount;i++)
                    {
                           HistoGramExt[i]
            =10*((float)iHistoGramExt[i])/(BmpSize/bitCount);
                    }
                }
            }
            然后再將CBasicBmpProcess中的InitHistoGram方法變?yōu)橄旅孢@樣:
            void CBasicBmpProcess::InitHistoGram()//初始化直方圖
            {
                
            int BmpSize=bi.biWidth*bi.biHeight,ColorCount=bitCount<<8;
                
            this->_histoGram.InitHistoGram(BmpSize,ColorCount,lpBuf,bitCount);

            }
            然后繼續(xù)使用Extract Class對(duì)CBasicBmpProcess進(jìn)行抽離。在此過(guò)程當(dāng)中,由于CBasicBmpProcess已經(jīng)失去了最初的功能,所以將它重命名為CBmpData。效果如下圖所示:

            調(diào)用舉例:
            void CBmpData::AveStrainWaves()//均值濾波
            {
                
            this->processor->AveStrainWaves();
                BmpStatsChanged
            =TRUE;
            }    



            (2)Extract SubClass
            class  中的某些特性只被某些(而非全部)實(shí)體用到。
            Extract Class和Extract Sub Class是重構(gòu)的兩種選擇,兩者之間的抉擇實(shí)際上就是委托和繼承之間的抉擇。Extract Sub Class通常更容易進(jìn)行,但它也有限制:一旦對(duì)象創(chuàng)建完成,你無(wú)法再改變與“型別相關(guān)的行為”。但如果使用Extract Class,你只需要插入另外一個(gè)不同組件,就可以改變對(duì)象行為。此外,subclass只能表現(xiàn)一組變化,如果你希望class以數(shù)種不同的方式變化,就必須使用委托。 
            作法:
            為Source Class定義一個(gè)新的Sub Class;
            為這個(gè)新的Sub Class定義構(gòu)造函數(shù):
                  簡(jiǎn)單的做法是:讓Sub Class構(gòu)造函數(shù)與Super Class有相同的參數(shù),并通過(guò)Super調(diào)用Super Class構(gòu)造函數(shù).
                  如果你希望對(duì)用戶隱藏Sub Class的存在,可以使用Replace Constructor With Factory Method.
            找出調(diào)用Super Class的所有地點(diǎn),如果它們需要的是新建的Sub Class,讓它們改而調(diào)用新的構(gòu)造函數(shù);
                  如果Sub Class的構(gòu)造函數(shù)和super class構(gòu)造函數(shù)的參數(shù)不同,可以使用Rename Method修改其參數(shù)列。如果Sub Class構(gòu)造函數(shù)不需要Super Class構(gòu)造函數(shù)的某些參數(shù),可以使用Rename Method將它們?nèi)コ?br>      (Rename Method:這里我的理解是:需要?jiǎng)?chuàng)建具體子類實(shí)例的時(shí)候,用一個(gè)名稱清晰的函數(shù)去創(chuàng)建它,并隱藏因?yàn)闃?gòu)造函數(shù)不同而導(dǎo)致實(shí)例化時(shí)的差別。)
                  如果不在需要直接實(shí)體化,就將它聲明為抽象類。
            逐步使用Push Down Method和Push Down Field將Source Class特性移到Sub Class中去。
                  和Extract Class不同的是,先處理函數(shù)再處理數(shù)據(jù),通常會(huì)簡(jiǎn)單一些。
                  當(dāng)一個(gè)public函數(shù)被下移到Sub Class后,你可能需要重新定義該函數(shù)的調(diào)用端的局部變量或參數(shù)型別,讓它們調(diào)用SubClass中的新函數(shù),如果忘記進(jìn)行這一步驟,編譯器會(huì)提醒你。
            找到所有這樣的值域:它們所傳達(dá)的信息如今可由繼承體系自身傳達(dá),以Self Encapsulate Field避免直接使用這些值域,然后將它們的取值函數(shù)替換為多態(tài)常量函數(shù),所有使用這些值域的地方都應(yīng)該以Replace Conditional with Polymorphism重構(gòu)。
                  (多態(tài)常量函數(shù):會(huì)在不同的subclass版本中返回不同的固定值)

            在我的圖像處理程序中,我發(fā)現(xiàn)圖像處理器類(CBmpProcessor)中存在一些可以分離的特性。比如均值濾波絕對(duì)不會(huì)和索貝爾邊緣檢測(cè)扯上什么關(guān)系,所以我打算對(duì)Processor類使用Extract Sub Class方法。讓它變?yōu)橄旅孢@種形式:

            然后把Process變?yōu)橐粋€(gè)純虛函數(shù),這樣,CBmpProcessor就成為一個(gè)抽象類了,同時(shí)還需要對(duì)調(diào)用方--即類CBasicBmpData進(jìn)行修改,另外由于這個(gè)修改改動(dòng)了CBasicBmpProcess暴露的公共方法,所以要調(diào)用這些方法的窗口中的事件處理函數(shù)也需要進(jìn)行一定修改。首先CBmpProcessor看上去會(huì)是這個(gè)樣子:

            這樣在SetBmpProcessor的函數(shù)體中配置Processor:
            void CBmpData::SetBmpProcessor(CBmpProcessor *processor)
            {
             this->processor=processor;
             this->processor->SetBitmapInfoHeader(bi);
             this->processor->SetRawData(lpBuf);
             this->processor->PrepareHistoGram(this->_histoGram);
             this->processor->PrepareProcessingData(this->lptmpBuf);
            }
            而調(diào)用Processor處理圖像就更簡(jiǎn)單了:
            void CBmpData::ProcessByProcessor(void)
            {
                
            this->processor->Process();
            }
            然后把窗口中的事件處理函數(shù)修改一下:
            void CMainFrame::OnSobel() 
            {
                
            // TODO: Add your command handler code here
                if(WindowCounter>0)
                {
                    CMDIChildWnd
            * pMDIActive = MDIGetActive();
                    CImageDoc
            * pDoc=(CImageDoc*)pMDIActive->GetActiveDocument();
                    pDoc
            ->bmp.SetBmpProcessor(new CSobel());
                    pDoc
            ->
            bmp.ProcessByProcessor();
                    pDoc->UpdateAllViews(NULL,0,NULL);
                }
                
            else
                    AfxMessageBox(
            "請(qǐng)先打開一幅位圖!");
                
            }

            完整的CBmpProcessor類圖:



            (3)Extract Interface
            若干客戶使用class接口中的同一子集;或者兩個(gè)classes的接口有部分相同。
            處理方法:將相同的子集提煉到一個(gè)獨(dú)立的接口中。
            在許多面向?qū)ο笳Z(yǔ)言中,這種“責(zé)任劃分能力”是通過(guò)多重繼承支持的,你可以針對(duì)一段行為建立一個(gè)class,再將它們組合于一份實(shí)現(xiàn)品中。java只提供單一繼承,但你可以運(yùn)用interface來(lái)昭示并實(shí)現(xiàn)這種需求。
            如果某個(gè)class在不同環(huán)境中扮演截然不同的角色,使用interface是一個(gè)好主意。
            作法:
            新建一個(gè)空接口;
            在接口中聲明待提煉類的共同操作;
            讓相關(guān)的class實(shí)現(xiàn)上述接口;
            調(diào)整客戶的型別聲明,使得以運(yùn)用該接口。

            (4)Duplicate Observed Data:如果你的Large Class是一個(gè)GUI class,你可能需要把數(shù)據(jù)和行為移動(dòng)到一個(gè)獨(dú)立的領(lǐng)域?qū)ο笕ァ?br>將Domain Data拷貝到一個(gè)domain object中,建立一個(gè)Observer模式,用以對(duì)Domain object和GUI object內(nèi)的數(shù)據(jù)進(jìn)行同步控制。
            一個(gè)分層良好的系統(tǒng),應(yīng)該將處理用戶界面和處理業(yè)務(wù)邏輯的代碼分開。之所以這樣做,原因有以下幾點(diǎn):
            (1)你可能需要使用數(shù)個(gè)不同的用戶界面來(lái)表現(xiàn)相同的業(yè)務(wù)邏輯:如果同時(shí)承擔(dān)兩種責(zé)任,用戶界面會(huì)變得過(guò)分復(fù)雜;
            (2)與GUI隔離之后,domain object的維護(hù)和演化都會(huì)更容易:你甚至可以讓不同的開發(fā)者負(fù)責(zé)不同部分的開發(fā)。
            作法:
            修改presentation class,使其成為domain class的觀察者。
            針對(duì)GUI class內(nèi)的domain data,使用Self Encapsulate Field。
            編譯,測(cè)試
            在事件處理函數(shù)中加上對(duì)設(shè)值函數(shù)的調(diào)用,以“直接訪問方式”更新GUI組件。
            編譯、測(cè)試。
            在domain class中定義數(shù)據(jù)及其相關(guān)的訪問函數(shù)。
            修改presentation class中的訪問函數(shù),將它們的操作對(duì)象改為domain object。
             修改observer的update,使其從相應(yīng)的domain object中所需的數(shù)據(jù)拷貝給GUI組件。
            編譯,測(cè)試。

             

            看上去很像.net2005中的DataObject。在前臺(tái)綁定GridView之后,可以將GridView中的一些行為(添加、修改、刪除)與DataObject進(jìn)行映射。比如:
            <asp:ObjectDataSource ID="ObjectDataSource2" runat="server" SelectMethod="GetUsersInRole"
                TypeName
            ="System.Web.Security.Roles" OldValuesParameterFormatString="original_{0}" OnSelecting="ObjectDataSource2_Selecting">
                
            <SelectParameters>
                    
            <asp:ControlParameter ControlID="GridView1" Name="roleName" PropertyName="SelectedValue"
                        Type
            ="String" />
                
            </SelectParameters>
            </asp:ObjectDataSource>
            代碼:
            [DataObject]
                
            public static class RoleDB
                {
                    [DataObjectMethod(DataObjectMethodType.Select)]
                    
            public static RoleInfoCollection GetAllRoles()
                    {
                        RoleInfoCollection infos 
            = new RoleInfoCollection();
                        
            string[] roleNames = Roles.GetAllRoles();
                        
            foreach (string roleName in roleNames)
                        {
                            RoleInfo info 
            = new RoleInfo();
                            info.Name 
            = roleName;
                            info.Symbol 
            = roleName;
                            infos.Add(info);
                        }
                        
            return infos;
                    }
            posted on 2007-07-01 00:10 littlegai 閱讀(428) 評(píng)論(0)  編輯 收藏 引用

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


            中文字幕久久精品| 91精品国产乱码久久久久久| 国产亚洲美女精品久久久| 亚洲国产精品久久久久婷婷软件 | 狠狠人妻久久久久久综合蜜桃 | 久久国产成人午夜aⅴ影院 | 狠狠色丁香久久婷婷综合蜜芽五月 | 久久精品国产99国产精品导航 | 久久精品国产免费观看 | 国产成人久久精品一区二区三区 | 99久久婷婷国产一区二区| 超级碰碰碰碰97久久久久| 久久精品成人国产午夜| 久久久久青草线蕉综合超碰 | 久久婷婷是五月综合色狠狠| 996久久国产精品线观看| 欧美黑人激情性久久| 久久精品18| 青青青国产成人久久111网站| 狠狠综合久久综合88亚洲| 久久国产香蕉一区精品| 久久午夜电影网| 久久精品人人做人人爽97| 久久乐国产综合亚洲精品| 久久久久99精品成人片| 99久久国产主播综合精品| 国产精品视频久久| AV色综合久久天堂AV色综合在| 热99RE久久精品这里都是精品免费| 久久综合九色综合久99| 久久精品国产第一区二区| 99久久99久久精品国产片| 国产精品美女久久久免费| 国产精品99久久久久久猫咪| 日本免费久久久久久久网站| 久久99精品国产| 亚洲天堂久久精品| 久久精品国产精品亚洲人人| 久久996热精品xxxx| 伊人色综合九久久天天蜜桃| 久久久久青草线蕉综合超碰|