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

            第一桶 從C到C++ 第九碗 陳老C演迭代開發(fā) 潘小P學(xué)漸進(jìn)編程(之四)

                 “嗯,我估計(jì)那個(gè)地方以后會(huì)火!”老C一邊回味,一邊肯定的說(shuō)道。
                 “呵呵,不知道以后還會(huì)不會(huì)像現(xiàn)在的菜量一樣上……”小P有些懷疑。
                 “哦,那我們就一直去,知道那一家菜量變小唄。”老C很有些主意,“剛吃完不要馬上坐下,我們走兩步吧。”
                 “好啊。”小P應(yīng)道。
                 兩個(gè)人從一村的天橋走回了東二……
                 “來(lái),我們來(lái)說(shuō)說(shuō)所謂的模塊是怎么回事。”老C喝了一口茶,又開始和小P聊起來(lái),“你看,我們的程序按照邏輯可以分成幾個(gè)部分,分別是apple game這個(gè)游戲,child queue這個(gè)游戲的內(nèi)容以及queue這個(gè)抽象的數(shù)據(jù)結(jié)構(gòu)。在這里我們以名詞為中心,將對(duì)這些名詞的操作分別提煉為函數(shù),放到不同的文件里。這樣我們就 可以認(rèn)為有了三個(gè)邏輯單元……”老C一邊說(shuō),一邊在白板上畫了三個(gè)框框,又用線條將它們連接在一起。

                 “看,這三個(gè)模塊有聯(lián)系,apple game與child queue有關(guān)系,而child queue與apple game 和queue都有聯(lián)系。這三個(gè)模塊接口的調(diào)用就表現(xiàn)出這種聯(lián)系關(guān)系,比如apple game的PlayGame()函數(shù)就調(diào)用了child queue的QueMoveToNextChild() 函數(shù),而child queue內(nèi)部又包含了queue這個(gè)抽象的數(shù)據(jù)結(jié)構(gòu)。這是一種新的思考問(wèn)題的方式,這種思考問(wèn)題的方法強(qiáng)調(diào)以數(shù)據(jù)為中心,而不是以操作為中心;這種思維 方法與我們熟悉的結(jié)構(gòu)化編程方法有些不同。”看到小P有些摸不到頭腦的樣子,老C決定打個(gè)比方,“就像我們的電腦,CPU,GPU和南北橋芯片各自獨(dú)立, 提供給外部一些接口,但是芯片內(nèi)部是如何處理數(shù)據(jù)的卻被封裝在內(nèi)部,我們需要的就是根據(jù)一定的規(guī)格將它們放到合適的地方就可以了,這樣就可以簡(jiǎn)單的大規(guī)模 生產(chǎn)了,只要你可以讀懂說(shuō)明書,你自己就可以組裝電腦;但是收音機(jī)就不是這樣——雖然它的技術(shù)復(fù)雜度遠(yuǎn)比電腦小——如果你要去處理沒有被封裝過(guò)的模擬電 路,那你首先得本科畢業(yè)。”
                 “唔……”小P點(diǎn)點(diǎn)頭。
                 “還有一個(gè)好處是復(fù)用,如果我們可以將queue這個(gè)模塊提煉出來(lái)——使得它保持合理的接口——那么它就可以被用到其它地方,只要這個(gè)地方需要隊(duì)列這樣一 個(gè)數(shù)據(jù)結(jié)構(gòu)。”老C撓撓頭,“但是在這里我們想要提煉這個(gè)queue數(shù)據(jù)結(jié)構(gòu)還是有些困難的,因?yàn)樵谶@里它不是一個(gè)典型的queue,或者說(shuō)它沒有表現(xiàn)出 queue的特質(zhì)。”
                 “為什么呢?”小P問(wèn)道。
                 “因?yàn)闆]有體現(xiàn)出FIFO的需求……queue這個(gè)東西還是用在需要first in first out的場(chǎng)合更合適些。”老C答道。
                 “哦,那么在這里應(yīng)當(dāng)使用什么數(shù)據(jù)結(jié)構(gòu)呢?”小P疑惑道。
                 “根據(jù)我的經(jīng)驗(yàn),此處使用linked list會(huì)好一些。”老C回答,“因?yàn)樵谶@個(gè)游戲里更多的是體現(xiàn)在某處插入和刪除數(shù)據(jù)……要不我們改寫一下我們的代碼,好讓你有個(gè)更清楚的認(rèn)識(shí)?”
                 “好啊,怎么更改呢?”小P問(wèn)道。
                 “我們把具體需要的動(dòng)作用函數(shù)來(lái)表達(dá),這樣你就可以更清楚的看到我們需要什么樣的數(shù)據(jù)結(jié)構(gòu)了。”老C說(shuō)道,“同樣我們還是需要偽代碼來(lái)幫忙。我們先試著不 要陷入為主的使用queue作為child queue的實(shí)現(xiàn),先用偽代碼將空白的地方添上,看看我們到底需要什么樣的數(shù)據(jù)結(jié)構(gòu)。”說(shuō)著老C開始更改代碼。

            applegame.c:

            #include "applegame.h"

            #include "mydebug.h"

            static void QueInitQueue (QUEUE* childQueue);
            static int QueIsChildExisted (QUEUE* childQueue);
            static void QueKickOutChild (QUEUE* childQueue);
            static void QueMoveToNextChild (QUEUE* childQueue);
            static int QueFindRemainedChild (QUEUE* childQueue);

            void InitAppleGame(APPLE_GAME* game)
            {
                MY_DEBUG("Init the apple game.\n");

                game->currCountNum_            = 0;
                game->kickOutNum_            = KICK_OUT_NUM;
                game->childrenRemained_        = CHILDREN_NUM;

                QueInitQueue(&(game->childrenQueue_));
            }

            int IsGameOver(APPLE_GAME* game)
            {
                MY_DEBUG_1("The children remained %d\n", game->childrenRemained_);

                return (1 == game->childrenRemained_);
            }

            void PlayGame(APPLE_GAME* game)
            {
                MY_DEBUG("Play game...\n");

                /* If the current child is existed in the queue, count on, then check if she will be kicked out. */
                if (QueIsChildExisted(&(game->childrenQueue_)))
                {
                    /* Count on. */
                    game->currCountNum_++;

                    /* If the child counts kicked out number, then she is kicked out. */
                    if (game->currCountNum_ == game->kickOutNum_)
                    {
                        QueKickOutChild(&(game->childrenQueue_));
                        game->childrenRemained_--;
                        game->currCountNum_ = 0;

                        MY_DEBUG_1("The child kicked out is %d\n", game->childrenQueue_.queue_[game->childrenQueue_.index_].seatNum_);
                    }
                }

                QueMoveToNextChild(&(game->childrenQueue_));
            }

            int LastChildSeatNum(APPLE_GAME* game)
            {
                int seatNum;

                MY_DEBUG("Searching last child's seat number\n");

                seatNum = QueFindRemainedChild(&(game->childrenQueue_));

                return seatNum;
            }


            /************************************************************************/
            /* Local functions                                                      */
            /************************************************************************/
            static void QueInitQueue(QUEUE* childQueue)
            {
                /* Insert all children into the game. */
                
                int i;

                childQueue->size_    = CHILDREN_NUM;
                childQueue->index_ = 0;
                for (i = 0; i < childQueue->size_; ++i)
                {
                    childQueue->queue_[i].seatNum_ = i + 1;
                    childQueue->queue_[i].existeState_ = EXISTED;
                }
            }

            static int QueIsChildExisted(QUEUE* childQueue)
            {
                /* Is the child existed in the game? */
                return (EXISTED == childQueue->queue_[childQueue->index_].existeState_);
            }

            static void QueKickOutChild(QUEUE* childQueue)
            {
                /* Remove the child from the game. */
                childQueue->queue_[childQueue->index_].existeState_ = ABSENT;
            }

            static void QueMoveToNextChild(QUEUE* childQueue)
            {
                /* Go to the next child. */
                childQueue->index_++;
                childQueue->index_ %= childQueue->size_;
            }

            static int QueFindRemainedChild(QUEUE* childQueue)
            {
                /* Find the last child remained. */
                int i;

                for (i = 0; i < childQueue->size_; ++i)
                {
                    if (EXISTED == childQueue->queue_[i].existeState_)
                    {
                        break;
                    }
                }

                return childQueue->queue_[i].seatNum_;    
            }

                 “看來(lái)我們更加需要一個(gè)便于在任意地方插入與刪除元素的數(shù)據(jù)結(jié)構(gòu),而不是便于在頭和尾部插入與刪除元素的數(shù)據(jù)結(jié)構(gòu)。”老C道,“首先我們了解了需求,現(xiàn)在我們看如何將這些需求組織成為我們代碼的單元模塊。”老C搓搓手,“這樣吧,我來(lái)寫,你來(lái)看。”說(shuō)著他開始改寫applegame.c,applegame.h。

            applegame.h:

            #if !defined(APPLE_GAME_H_)
            #define APPLE_GAME_H_

            #include "list.h"

            #define CHILDREN_NUM    20U
            #define KICK_OUT_NUM    7U


            typedef struct tagAPPLE_GAME
            {
                int currCountNum_;
                int kickOutNum_;    
                LIST childrenList_;
            }APPLE_GAME;

            extern void InitAppleGame (APPLE_GAME* game);
            extern int  IsGameOver (APPLE_GAME* game);
            extern void PlayGame (APPLE_GAME*  game);
            extern int  LastChildSeatNum (APPLE_GAME* game);

            #endif /* APPLE_GAME_H_ */

            ------------------------------------------------------(華麗的分割線)

            applegame.c:

            #include "applegame.h"
            #include "mydebug.h"

            #include <assert.h>
            #include <stdlib.h>

            static void    ChListInitChildList (LIST* childList);
            static void ChListKickOutChild (LIST* childList);
            static void ChListMoveToNextChild (LIST* childList);
            static int ChListFindRemainedChild (LIST* childList);

            void InitAppleGame(APPLE_GAME* game)
            {
                MY_DEBUG("Init the apple game.\n");

                game->kickOutNum_    = KICK_OUT_NUM;
                game->currCountNum_ = 0;
                
                ChListInitChildList(&(game->childrenList_));
            }

            int IsGameOver(APPLE_GAME* game)
            {
                int childRemained;

                childRemained = ListSize(&(game->childrenList_));
                
                MY_DEBUG_1("The children remained %d\n", childRemained);

                return 1 == childRemained;
            }

            void PlayGame(APPLE_GAME* game)
            {
                MY_DEBUG("Play game...\n");

                ++game->currCountNum_;    

                if (game->kickOutNum_ == game->currCountNum_)
                {
                    /* When kick out child, the index automatically points to the next child. */
                    ChListKickOutChild(&(game->childrenList_));        
                    
                    game->currCountNum_ = 0;
                }
                else
                {
                    ChListMoveToNextChild(&(game->childrenList_));
                }
            }

            int LastChildSeatNum(APPLE_GAME* game)
            {
                int seatNum;

                MY_DEBUG("Searching last child's seat number\n");

                seatNum = ChListFindRemainedChild(&(game->childrenList_));

                return seatNum;
            }


            /************************************************************************/
            /* Local functions                                                      */
            /************************************************************************/
            static void ChListInitChildList( LIST* childList )
            {
                /* Insert all children into the game. */

                int i;
                LIST_NODE* child;

                /* Make sure that the game at least has one child */
                assert(0 != CHILDREN_NUM);

                ListInitList(childList);

                for (i = 0; i < CHILDREN_NUM; ++i)
                {
                    child = (LIST_NODE*)malloc(sizeof(LIST_NODE));
                    child->content_.seatNum_ = i + 1;

                    /* Attach a new node to the end of the list. */
                    ListPushBack(childList, child);
                }

                childList->index_ = ListBegin(childList);
            }


            static void ChListKickOutChild( LIST* childList )
            {
                MY_DEBUG_1("The child kicked out is %d\n", childList->index_->content_.seatNum_);

                /* Remove a node the index pointing to. */
                childList->index_ = ListErase(childList, childList->index_);
            }

            static void ChListMoveToNextChild( LIST* childList )
            {
                /* Index goes to the next node. */
                ListForward(childList);
            }

            static int ChListFindRemainedChild( LIST* childList )
            {
                LIST_NODE* iter;
                int seatNum;

                /* Get the first node of the list. */
                iter = ListBegin(childList);
                seatNum = iter->content_.seatNum_;

                /* Destroy the list. */
                ListClear(childList);

                return seatNum;
            }



               “ 看,我們現(xiàn)在把linked list數(shù)據(jù)結(jié)構(gòu)從apple game的數(shù)據(jù)聲明中拿出,并認(rèn)為它應(yīng)當(dāng)出現(xiàn)在list.h中。然后根據(jù)我們的使用環(huán)境,在child list模塊中提出對(duì)抽象的linked list的具體要求,也就是我們?cè)诰帉懖僮鱟hild list時(shí)希望list數(shù)據(jù)結(jié)構(gòu)所具有的接口。”老C指著代碼向小P解釋,“比如我們?cè)诰帉慶hild list的初始化函數(shù)ChListInitChildList()時(shí),我們希望list模塊提供相應(yīng)的初始化函數(shù)ListInitList(),因?yàn)樽鳛槭褂谜撸覀儫o(wú)需知道也不可能知道list模塊是怎么樣初始化的。其它的接口也一樣,它們都是根據(jù)具體的應(yīng)用環(huán)境需求提出來(lái)的。”老C揉揉發(fā)酸的手指,“現(xiàn)在我們已經(jīng)有了具體的對(duì)list模塊的需求,就可以根據(jù)這些要求對(duì)linked list這個(gè)模塊進(jìn)行編碼了……”
                 “等等,”小P插嘴道,“ChListInitChildList()函數(shù)中的assert()是什么意思?”
                 “哦,這是一種編程習(xí)慣,用于查找程序中違反編程約定的地方。一般的程序在運(yùn)行的時(shí)候有各種各樣的不變性條件,分為前置不變性,運(yùn)行不變性和后置不變性條 件,assert()用于檢測(cè)這些條件是否滿足我們編程的約定。因?yàn)槲覀兊膌inked list不能出現(xiàn)為0的情況,且linked list的大小實(shí)在程序中用宏定義出來(lái)的,所以在初始化的時(shí)候我使用斷言確保初始化程序可以在正常的條件下進(jìn)行。但是如果linked list是由外部輸入,比如鍵盤輸入得到,那么我們最好使用一個(gè)if()判斷來(lái)處理用戶輸入的異常情況……總之看你認(rèn)為這些反常的情況是出現(xiàn)在程序內(nèi)部還 是外部——這些我以后還會(huì)談到的。關(guān)于斷言,你可以在網(wǎng)上搜索一下。”老C解釋。
                 “好的……又是一個(gè)需要經(jīng)驗(yàn)積累的東西嗎?”小P問(wèn)。
                 “呵呵,可以這樣說(shuō),”老C點(diǎn)點(diǎn)頭,“你先大概看看代碼的結(jié)構(gòu),不必糾纏于細(xì)節(jié),我們?cè)賮?lái)看看linked list的具體實(shí)現(xiàn)。”說(shuō)完老C新建了list.h和list.c兩個(gè)文件,然后又開始扣扣扣扣的打字了。

            list.h:

            #if !defined(LIST_H_)
            #define LIST_H_

            typedef int SEAT_NUM;
            typedef enum tagEXISTE_STATE { ABSENT, EXISTED } EXISTE_STATE;
            typedef struct tagCHILD
            {
                SEAT_NUM        seatNum_;
            }CHILD;

            typedef CHILD LIST_CONTENT;

            struct tagLIST_NODE
            {
                struct tagLIST_NODE*    prev_;
                struct tagLIST_NODE*    next_;
                LIST_CONTENT            content_;
            };

            typedef struct tagLIST_NODE LIST_NODE;

            typedef struct tagLIST
            {
                int               size_;
                LIST_NODE*        index_;
                LIST_NODE*        list_;
            }LIST;

            extern void           ListInitList (LIST* list);
            extern int            ListSize (LIST* list);
            extern LIST_NODE*     ListBegin (LIST* list);
            extern LIST_NODE*     ListEnd (LIST* list);
            extern void           ListInsert (LIST* list, LIST_NODE* iter, LIST_NODE* newNode);
            extern LIST_NODE*     ListErase (LIST* list, LIST_NODE* iter);
            extern void           ListClear (LIST* list);
            extern void           ListPushBack (LIST* list, LIST_NODE* newNode);
            extern void           ListPopFront (LIST* list);
            extern LIST_NODE*     ListForward (LIST* list);

            #endif /* LIST_H_ */

            ------------------------------------------------------(華麗的分割線)

            list.c:

            #include "list.h"

            #include <stdlib.h>
            #include <string.h>


            /* Create the list head. */
            void ListInitList(LIST* list)
            {
                LIST_NODE* head;

                head = (LIST_NODE*)malloc(sizeof(LIST_NODE));

                head->next_ = head;
                head->prev_ = head;

                list->size_        = 0;
                list->list_        = head;
                list->index_    = list->list_;
            }

            /* Return the size of the list. (Does not include list head.) */
            int    ListSize(LIST* list)
            {
                return list->size_;
            }

            /* Return the first node of the list. */
            LIST_NODE* ListBegin(LIST* list)
            {
                return list->list_->next_;
            }

            /* Return the node after the last node of the list */
            LIST_NODE* ListEnd(LIST* list)
            {
                return list->list_;
            }

            /* Insert a new node before the specified list node. */
            void ListInsert( LIST* list, LIST_NODE* iter, LIST_NODE* newNode )
            {
                list = list;

                newNode->next_            = iter;
                newNode->prev_            = iter->prev_;
                newNode->prev_->next_    = newNode;
                iter->prev_                = newNode;

                ++list->size_;
            }

            /* Remove a list node specified. */
            LIST_NODE* ListErase(LIST* list, LIST_NODE* iter)
            {
                LIST_NODE* nextNode;
                
                list = list;
                nextNode = iter->next_;
                if (ListEnd(list) == nextNode)
                {
                    nextNode = ListBegin(list);
                }

                iter->prev_->next_ = iter->next_;
                iter->next_->prev_ = iter->prev_;

                memset(iter, 0, sizeof(LIST_NODE));

                free(iter);

                --list->size_;

                return nextNode;
            }

            /* Destroy all nodes of the list, including the head. */
            void ListClear(LIST* list)
            {
                while (ListSize(list))
                {
                    ListPopFront(list);
                }

                memset(list->list_, 0, sizeof(LIST_NODE));

                free(list->list_);
            }

            /* Attach a new node to the end of the list. */
            void ListPushBack( LIST* list, LIST_NODE* newNode )
            {
                ListInsert(list, ListEnd(list), newNode);
            }

            /* Remove the fist node of the list. */
            void ListPopFront(LIST* list)
            {
                if (ListSize(list))
                {
                    ListErase(list, ListBegin(list));
                }
            }

            /* Move the list index to the next node. If reaches the one after the last node,
               the index is moved to the first node. */
            LIST_NODE* ListForward(LIST* list)
            {
                list->index_ = list->index_->next_;

                if (ListEnd(list) == list->index_)
                {
                    list->index_ = ListBegin(list);
                }

                return list->index_;
            }


                 “在這里我們實(shí)現(xiàn)了一個(gè)雙向鏈表,因?yàn)檫@樣更容易添加和刪除元素;這個(gè)雙向鏈表有一個(gè)頭指針,在list結(jié)構(gòu)體里邊叫l(wèi)ist_,這樣在刪除和添加元素的 時(shí)候比較好處理;注意在這里我們使用了左閉右開定義域,就是說(shuō)使用了[first, last)的形式定義鏈表的定義域,這樣在計(jì)算長(zhǎng)度和循環(huán)迭代上都很方便,關(guān)于這方面的內(nèi)容我們以后還會(huì)討論到……”老C撓 撓頭,接著說(shuō)道,“注意在刪除內(nèi)存前將其內(nèi)容清0是很好的習(xí)慣,這樣在數(shù)據(jù)敗壞時(shí)程序會(huì)迅速崩潰,從而減少大量的調(diào)試時(shí)間。”他又看了看代碼,“list = list這樣奇怪的用法只是為了去除編譯警告——認(rèn)真的去除所有編譯警告是也是比較好的習(xí)慣——為什么要在函數(shù)形參設(shè)置多余的參數(shù)?……為了統(tǒng)一,這樣可 以減少一些記憶力上的負(fù)擔(dān)……喔,iter是iterator的簡(jiǎn)寫……”老C給小P簡(jiǎn)單的講解了一下。
                 “等等,我再仔細(xì)看看……”小P開始在屏幕上翻來(lái)翻去,滾動(dòng)鼠標(biāo)的滾動(dòng)軸,“嗯,這下我有些明白了,apple game使用了child list作為實(shí)現(xiàn),apple game的函數(shù)——就是main.c中的函數(shù)——調(diào)用了child list的接口作為實(shí)現(xiàn)的一部分,而child list的接口就是apple game中的static函數(shù)……然后child list又使用linked list這個(gè)模塊的接口作為其實(shí)現(xiàn)的一部分……嗯,層次結(jié)構(gòu)就是這樣……”
                 “沒錯(cuò),我們將功能分配到各個(gè)模塊中,最后得到了一個(gè)和具體需求——也就是apple game——獨(dú)立的模塊,就是linked list,這個(gè)模塊完全不知道apple game的任何信息,這樣——的確很有趣——我們實(shí)現(xiàn)的linked list有可能被用在別處。”老C接著補(bǔ)充道。
                 “等等等等,”小P叫住老C,“我還得再看看代碼,熟悉熟悉,學(xué)習(xí)學(xué)習(xí)……”他又看了幾遍代碼,重點(diǎn)看了看linked list的實(shí)現(xiàn),“不錯(cuò),只要我們將list.h和list.c加入到其他項(xiàng)目,并修改LIST_CONTENT這個(gè)類型的定義,的確可以將這些代碼用在 其他地方……”
                 “沒錯(cuò),由于我們模塊的粒度……什么叫粒度?就是規(guī)模……由于我們模塊的規(guī)模足夠細(xì)致,這樣我們有一部分的代碼就可以被復(fù)用,而且這些代碼是經(jīng)過(guò)我們這個(gè)項(xiàng)目測(cè)試過(guò)的,完全可以用在其他地方以減少開發(fā)和測(cè)試的工作量。”老C習(xí)慣性的總結(jié)道。
                 “但是……等等……好像list模塊最多只能用一次,因?yàn)闆]有辦法在一個(gè)工程中定義兩個(gè)LIST_CONTENT類型……”小P突然注意到什么,很是興奮的分析道。
                 “呵呵,這個(gè)是下一步的工作了,你先看看這一版的實(shí)現(xiàn)是否正確……”老C開始有些佩服小P的觀察力了。
                 “嗯,我來(lái)看看程序是否工作正常……”小P修改了applegame.hCHILDREN_NUMKICK_OUT_NUM 宏的定義,觀察了調(diào)試信息的輸出,“嗯,我認(rèn)為沒有什么問(wèn)題。”他點(diǎn)頭說(shuō)道。
                 “注意值為1的邊界情況和0的非正常情況。”老C提醒。
                 “知道了,”小P應(yīng)道,又嘗試了一些邊界情況和非正常情況,“嗯,程序的執(zhí)行沒有什么問(wèn)題,在0值的情況下會(huì)出現(xiàn)runtime錯(cuò)誤。”
                 “好啦,我們的C編碼活動(dòng)告一段落啦。”老C說(shuō)完將所有文件拷貝到AppleGame_V1.02,然后將AppleGame_V1.02命名為AppleGame_V2.0。“我們已經(jīng)進(jìn)行了簡(jiǎn)單的開發(fā)活動(dòng),讓我們來(lái)總結(jié)總結(jié)……”
                 “是么?有哪些需要總結(jié)的地方?”小P摸摸頭,問(wèn)道。
                 “首先是思想和原理性的東西,其次是方法上的東西。”老C道。
                 “?槑”小P有些莫名其妙。
                 “呵呵,我來(lái)說(shuō)你來(lái)寫吧……把白板擦干凈……”老C揉揉手指,決定偷懶一下。
                 “好!”
                 老C接過(guò)彩筆,在白板中間從上到下畫了一道線,左邊寫上思想,右邊寫上方法。“你先寫寫思想上的東西吧,”他喝了一口水,“思想是最重要的,我們需要通過(guò) 學(xué)習(xí)語(yǔ)言來(lái)學(xué)習(xí)思想——只要學(xué)會(huì)了編程的思想,那么你再學(xué)習(xí)其他任何語(yǔ)言都會(huì)很快——要深入語(yǔ)言去學(xué)習(xí),而不是只是使用語(yǔ)言。首先我們的第一個(gè)經(jīng)驗(yàn)是,以 數(shù)據(jù)為中心思考問(wèn)題,而不是以活動(dòng)為中心思考問(wèn)題。”
                 “嗯,好像沒有什么問(wèn)題,如果我們以數(shù)據(jù)為中心思考問(wèn)題,那么總會(huì)抽象出一些變化較少的,相對(duì)穩(wěn)定的數(shù)據(jù),將對(duì)數(shù)據(jù)的操作與數(shù)據(jù)捆綁到一個(gè)代碼單元中,這樣就可以有限度的復(fù)用已經(jīng)開發(fā)的代碼……”小P若有所思。
                 “呵呵,這只是一個(gè)好處,還有一些其他的好處,需要你在以后的編程中體會(huì)。”老C笑笑。這樣白板的左邊出現(xiàn)了第一個(gè)和第二個(gè)經(jīng)驗(yàn)的總結(jié)。

            思想:
            1. 以數(shù)據(jù)為中心思考問(wèn)題的解決之道。
            2. 將對(duì)同類數(shù)據(jù)的操作放在相同的編譯單元中。

                 “唔,如果我們使用這種方法去解決問(wèn)題,是不是就是面向?qū)ο罅四兀?#8221;小P突然想到了什么,問(wèn)老C。
                 “哦,還不是。因?yàn)槊嫦驅(qū)ο蟮娜筇匦裕庋b、繼承和多態(tài),我們只使用了封裝這個(gè)特性,所以不能叫面向?qū)ο蟮模荒芙谢趯?duì)象的。”老C答道,“所以我們第三個(gè)總結(jié),就是將對(duì)數(shù)據(jù)的操作,如非必要,盡量隱藏起來(lái),而不要暴露給其他編譯單元或者用戶。”
                 “好哩。”小P又飛快的在下面加上了第三條經(jīng)驗(yàn)。

            思想:
            1. 以數(shù)據(jù)為中心思考問(wèn)題的解決之道。
            2. 將對(duì)同類數(shù)據(jù)的操作放在相同的編譯單元中。
            3. 信息隱藏,而不是暴露,以將問(wèn)題的規(guī)模控制在可理解的范圍。

                 “還有什么?”小P問(wèn)。
                 “這三條已經(jīng)可以了,夠你體會(huì)一陣子了。貪多嚼不爛,我們就先總結(jié)這么多吧。”老C無(wú)奈道,“編程的思想是很難學(xué)習(xí)的,需要經(jīng)驗(yàn)來(lái)體會(huì)——不要妄圖一次學(xué) 會(huì)所有的東西,學(xué)習(xí)任何東西都是螺旋形上升的,編程也一樣……就連我們的開發(fā)過(guò)程也一樣……好啦,我們?cè)賮?lái)總結(jié)總結(jié)方法吧。”
                 “那么我就寫在右邊了?”小P問(wèn)。
                 “是啊,當(dāng)然了。”老C囧道,“我們的方法你也看到了,其實(shí)也就是那么幾條。首先要從大方面著手,將問(wèn)題分解為幾個(gè)基本的步驟,然后用偽代碼寫出解決問(wèn)題的思路。”
                 “哦,是這樣的。”小P迫不及待的在白板左面寫下第一條關(guān)于方法的總結(jié)。

            方法:
            1. 自上向下的編程,而不是自下向上。

                 “不,不是這樣,”老C阻止小P,“無(wú)所謂自上向下和自下向上,因?yàn)樵陂_發(fā)過(guò)程中這兩個(gè)活動(dòng)是并發(fā)的并且始終貫穿于開發(fā)活動(dòng)當(dāng)中,甚至有人說(shuō)設(shè)計(jì)要自上向下,實(shí)現(xiàn)要自下向上。”
                 “那么應(yīng)當(dāng)怎么總結(jié)方法呢?”小P郁悶道。
                 “首先著眼于整體,而不是細(xì)節(jié)。”老C得意的說(shuō)道。
                 “好,我不反對(duì)。”于是小P將白板右面的文字進(jìn)行了更改。

            方法:
            1. 首先關(guān)注整體,而不是細(xì)節(jié)。

                 “然后呢?”小P問(wèn)。
                 “然后使用偽代碼,寫出問(wèn)題的解決之道。”老P回答,“然后再根據(jù)偽代碼,寫出相應(yīng)的數(shù)據(jù)定義以及需要解決此問(wèn)題的對(duì)數(shù)據(jù)的操作,并寫出測(cè)試代碼,讓我們的框架可以迅速編譯通過(guò)并運(yùn)行。”
                 “好,那么我就這樣總結(jié)。”小P在白板上涂涂抹抹。

            方法:
            1. 首先關(guān)注整體,而不是細(xì)節(jié)。
            2. 先考慮測(cè)試,更先于編碼。
            3. 先用偽代碼編程,以體現(xiàn)程序的意圖,再用具體代碼完善之。
            4. 迅速構(gòu)建可編譯通過(guò)的,并且可執(zhí)行的框架程序,使用測(cè)試代碼測(cè)試之。

                 “如果我們做到以上幾點(diǎn),我們可以迅速擁有一個(gè)正確的可以運(yùn)行的框架,以后寫的代碼都可以在此框架下進(jìn)行測(cè)試,避免了后期進(jìn)行大規(guī)模代碼集成的風(fēng)險(xiǎn)。”老C道,“這些等你編碼規(guī)模變大以后就會(huì)有一些體會(huì)了。”
                 “是啊,現(xiàn)在我們已經(jīng)有了框架了,然后怎么辦呢?”小P問(wèn)道。
                 “因?yàn)槲覀円呀?jīng)將問(wèn)題分解為幾個(gè)部分了,對(duì)每一個(gè)部分反復(fù)運(yùn)用我們以上總結(jié)的四個(gè)經(jīng)驗(yàn),這樣我們就得到了更細(xì)小的模塊,遞歸下去,直到每個(gè)模塊的規(guī)模都在我們的掌控之中,我們就解決問(wèn)題了。”老C回答到。
                 “是啊是啊,這個(gè)很像分而治之的思想。”小P道。
                 “沒錯(cuò),你總結(jié)的很到位,這就是分而治之思想的一個(gè)運(yùn)用而已。”老C開始有些佩服小P的總結(jié)能力和聯(lián)想能力了,“還有一些方法需要總結(jié),比如說(shuō)要在代碼環(huán) 境中提取對(duì)其他模塊接口的需求,而不要自底向上的猜測(cè)某一模塊的接口應(yīng)當(dāng)是什么樣子。我們?cè)趯?shí)現(xiàn)linked list模塊就是用這種方法做的,我不管linked list究竟有多少接口,我只管在applegame.c的函數(shù)中實(shí)現(xiàn)算法,在其中自然而然的就會(huì)有對(duì)linked list接口函數(shù)的需求。根據(jù)這些需求,我們先寫出.h文件,然后拋開雜念專心致志的在其對(duì)應(yīng)的.c文件中實(shí)現(xiàn)這些外部對(duì)linked list接口的需要,而模塊外面沒有表現(xiàn)出來(lái)的一些更加具體的操作,自然被隱藏起來(lái)成為linked list內(nèi)部的static函數(shù)。不過(guò)在我們的代碼中,還沒有這樣的被隱藏起來(lái)的static函數(shù)而已,但如果我們繼續(xù)深入的優(yōu)化linked list模塊,這些函數(shù)遲早會(huì)出現(xiàn)的。”
                 “嗯,那么我們可以這么總結(jié)。”小P回應(yīng)道。

            方法:
            1. 首先關(guān)注整體,而不是細(xì)節(jié)。
            2. 先考慮測(cè)試,更先于編碼。
            3. 先用偽代碼編程,以體現(xiàn)程序的意圖,再用具體代碼完善之。
            4. 迅速構(gòu)建可編譯通過(guò)的,并且可執(zhí)行的框架程序,使用測(cè)試代碼測(cè)試之。
            5. 將以上方法反復(fù)應(yīng)用于子模塊,直到問(wèn)題被解決。
            6. 在上下文環(huán)境中提取對(duì)其他模塊的需求。
            7. 先寫.h文件,后寫.c文件。

                 “這樣就可以了吧。”小P問(wèn)。
                 “嗯,還差一些,你有沒有觀察到我們的程序經(jīng)歷了好幾個(gè)版本?而每一個(gè)版本都比上一個(gè)版本稍微好上一點(diǎn)點(diǎn)?”老C問(wèn)道。
                 “的確是這樣,那么這點(diǎn)應(yīng)當(dāng)如何總結(jié)呢?”小P問(wèn)。
                 “嗯,就是先快速實(shí)現(xiàn)一個(gè)可行的解法,然后放在實(shí)際需求環(huán)境中考察這個(gè)解法,根據(jù)實(shí)際需求對(duì)解法的反饋,不斷修正此程序。”老C回答,“簡(jiǎn)單的說(shuō)就是迭代的開發(fā),哲學(xué)一些就是螺旋形上升的開發(fā)。”
                 “我還是喜歡更通俗一些的說(shuō)法。”小P在白板上又增加了一條。

            方法:
            1. 首先關(guān)注整體,而不是細(xì)節(jié)。
            2. 先考慮測(cè)試,更先于編碼。
            3. 先用偽代碼編程,以體現(xiàn)程序的意圖,再用具體代碼完善之。
            4. 迅速構(gòu)建可編譯通過(guò)的,并且可執(zhí)行的框架程序,使用測(cè)試代碼測(cè)試之。
            5. 將以上方法反復(fù)應(yīng)用于子模塊,直到問(wèn)題被解決。
            6. 在上下文環(huán)境中提取對(duì)其他模塊的需求。
            7. 先寫.h文件,后寫.c文件。
            8. 先快速實(shí)現(xiàn)一個(gè)可行的解法,再根據(jù)反饋信息優(yōu)化之。

                 “呵呵,其實(shí)前面幾步我們是在做架構(gòu)設(shè)計(jì),也就是概要設(shè)計(jì),后面幾步是在進(jìn)行詳細(xì)設(shè)計(jì)及編碼,一般很少有一次就搞定的事情,比較好的解決之道總是這樣反復(fù) 來(lái)上幾次才找到的——除非你所在的團(tuán)隊(duì)對(duì)問(wèn)題的領(lǐng)域特別熟悉,對(duì)需求了解相當(dāng)透徹。”老C總結(jié),“好啦,時(shí)候也不早了,我們回去睡覺吧,過(guò)幾天我們來(lái)看看 用C++如何解決這個(gè)問(wèn)題。”
                 “好吧,你先回去吧,我把白板上的東西抄回去,再對(duì)照著我們寫過(guò)的代碼仔細(xì)琢磨琢磨。”小P道。
                 “哈哈,算了算了,明天再搞吧,今天已經(jīng)晚了。勞逸結(jié)合才是正道,不如你今晚和我再打打魔獸吧……”老C笑道。
                 “……也好……看我再給你支幾招……”小P笑道。
                 “呵呵,那就趕快回去吧,我的手有些癢癢了……”老C拉起小P,飛快的走出教研室。

            (想知道C++版本的實(shí)現(xiàn)么?)

            posted on 2009-02-18 23:49 Anderson 閱讀(1783) 評(píng)論(6)  編輯 收藏 引用

            評(píng)論

            # re: 第一桶 從C到C++ 第八碗 陳老C演迭代開發(fā) 潘小P學(xué)漸進(jìn)編程(之四) 2009-02-19 00:09 Anderson

            出了一趟遠(yuǎn)門,深圳、北京的跑了一圈……  回復(fù)  更多評(píng)論   

            # re: 第一桶 從C到C++ 第八碗 陳老C演迭代開發(fā) 潘小P學(xué)漸進(jìn)編程(之四) 2009-02-19 00:37 likenk

            難怪。
            等了這篇等了好久。哈哈,希望以后能天天讀到你的這個(gè)系列的文章。  回復(fù)  更多評(píng)論   

            # re: 第一桶 從C到C++ 第八碗 陳老C演迭代開發(fā) 潘小P學(xué)漸進(jìn)編程(之四) 2009-02-19 09:40 guest

            這個(gè)應(yīng)該算是第九碗了  回復(fù)  更多評(píng)論   

            # re: 第一桶 從C到C++ 第八碗 陳老C演迭代開發(fā) 潘小P學(xué)漸進(jìn)編程(之四)[未登錄] 2009-02-19 09:41 崔友志

            是啊,樓主的文章無(wú)論從寫作風(fēng)格還是內(nèi)容上都很不錯(cuò)
            期待連載  回復(fù)  更多評(píng)論   

            # re: 第一桶 從C到C++ 第九碗 陳老C演迭代開發(fā) 潘小P學(xué)漸進(jìn)編程(之四) 2009-02-19 11:34 Anderson

            @guest
            的確,出了一趟門,居然忘了吃幾碗飯了,呵呵。已經(jīng)更正了。
            又在list.c的函數(shù)前面增加了一些說(shuō)明性的文字,希望不熟悉STL的xdjm們看起來(lái)可以更清楚一些……  回復(fù)  更多評(píng)論   

            # re: 第一桶 從C到C++ 第九碗 陳老C演迭代開發(fā) 潘小P學(xué)漸進(jìn)編程(之四) 2009-02-22 06:13 ALLEN

            很適合我這種半生不熟的家伙,真乃雪中送炭之舉啊,辛苦了高手!  回復(fù)  更多評(píng)論   


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


            <2010年9月>
            2930311234
            567891011
            12131415161718
            19202122232425
            262728293012
            3456789

            導(dǎo)航

            統(tǒng)計(jì)

            常用鏈接

            留言簿(6)

            隨筆檔案(21)

            文章檔案(1)

            搜索

            最新評(píng)論

            閱讀排行榜

            評(píng)論排行榜

            久久精品国产99国产电影网 | 久久久久av无码免费网| 青青草原综合久久大伊人导航 | 久久精品国产半推半就| 久久精品免费一区二区三区| 亚洲伊人久久大香线蕉苏妲己| 国产成人精品久久一区二区三区av | 人人狠狠综合久久亚洲| 一级a性色生活片久久无| 婷婷综合久久中文字幕蜜桃三电影| 99re久久精品国产首页2020| 色综合久久综精品| 欧美精品丝袜久久久中文字幕| 要久久爱在线免费观看| 国产午夜福利精品久久2021| 久久精品国产一区二区三区日韩| 久久久久国色AV免费观看| 久久天天躁狠狠躁夜夜不卡| 精品久久久久久久久中文字幕| 亚洲国产天堂久久久久久| 国产成人久久精品一区二区三区| 久久黄视频| 久久不见久久见免费视频7| 精品国产青草久久久久福利| 97久久国产综合精品女不卡| 色综合久久最新中文字幕| 国产69精品久久久久APP下载 | 五月丁香综合激情六月久久| 99久久综合狠狠综合久久| 九九精品久久久久久噜噜| 久久精品国产69国产精品亚洲| 伊人久久大香线蕉成人| 久久99国产精一区二区三区| 色天使久久综合网天天| 久久综合九色综合久99 | 亚洲国产成人久久精品动漫| 无码人妻久久一区二区三区蜜桃 | 亚洲国产精品狼友中文久久久| 97久久精品午夜一区二区| 亚洲精品美女久久久久99| 久久综合九色综合欧美就去吻|