青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

第一桶 從C到C++ 第九碗 陳老C演迭代開(kāi)發(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喝了一口茶,又開(kāi)始和小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)比電腦小——如果你要去處理沒(méi)有被封裝過(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ō)它沒(méi)有表現(xiàn)出 queue的特質(zhì)。”
     “為什么呢?”小P問(wèn)道。
     “因?yàn)闆](méi)有體現(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開(kāi)始更改代碼。

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ō)著他開(kāi)始改寫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è)文件,然后又開(kāi)始扣扣扣扣的打字了。

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í)候比較好處理;注意在這里我們使用了左閉右開(kāi)定義域,就是說(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開(kāi)始在屏幕上翻來(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)就是這樣……”
     “沒(méi)錯(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è)類型的定義,的確可以將這些代碼用在 其他地方……”
     “沒(méi)錯(cuò),由于我們模塊的粒度……什么叫粒度?就是規(guī)模……由于我們模塊的規(guī)模足夠細(xì)致,這樣我們有一部分的代碼就可以被復(fù)用,而且這些代碼是經(jīng)過(guò)我們這個(gè)項(xiàng)目測(cè)試過(guò)的,完全可以用在其他地方以減少開(kāi)發(fā)和測(cè)試的工作量。”老C習(xí)慣性的總結(jié)道。
     “但是……等等……好像list模塊最多只能用一次,因?yàn)闆](méi)有辦法在一個(gè)工程中定義兩個(gè)LIST_CONTENT類型……”小P突然注意到什么,很是興奮的分析道。
     “呵呵,這個(gè)是下一步的工作了,你先看看這一版的實(shí)現(xiàn)是否正確……”老C開(kāi)始有些佩服小P的觀察力了。
     “嗯,我來(lái)看看程序是否工作正常……”小P修改了applegame.hCHILDREN_NUMKICK_OUT_NUM 宏的定義,觀察了調(diào)試信息的輸出,“嗯,我認(rèn)為沒(méi)有什么問(wèn)題。”他點(diǎn)頭說(shuō)道。
     “注意值為1的邊界情況和0的非正常情況。”老C提醒。
     “知道了,”小P應(yīng)道,又嘗試了一些邊界情況和非正常情況,“嗯,程序的執(zhí)行沒(méi)有什么問(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)單的開(kāi)發(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)題。”
     “嗯,好像沒(méi)有什么問(wèn)題,如果我們以數(shù)據(jù)為中心思考問(wèn)題,那么總會(huì)抽象出一些變化較少的,相對(duì)穩(wěn)定的數(shù)據(jù),將對(duì)數(shù)據(jù)的操作與數(shù)據(jù)捆綁到一個(gè)代碼單元中,這樣就可以有限度的復(fù)用已經(jīng)開(kāi)發(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í)任何東西都是螺旋形上升的,編程也一樣……就連我們的開(kāi)發(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)樵陂_(kāi)發(fā)過(guò)程中這兩個(gè)活動(dòng)是并發(fā)的并且始終貫穿于開(kāi)發(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道。
     “沒(méi)錯(cuò),你總結(jié)的很到位,這就是分而治之思想的一個(gè)運(yùn)用而已。”老C開(kāi)始有些佩服小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文件,然后拋開(kāi)雜念專心致志的在其對(duì)應(yīng)的.c文件中實(shí)現(xiàn)這些外部對(duì)linked list接口的需要,而模塊外面沒(méi)有表現(xiàn)出來(lái)的一些更加具體的操作,自然被隱藏起來(lái)成為linked list內(nèi)部的static函數(shù)。不過(guò)在我們的代碼中,還沒(méi)有這樣的被隱藏起來(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)。
     “嗯,還差一些,你有沒(méi)有觀察到我們的程序經(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ō)就是迭代的開(kāi)發(fā),哲學(xué)一些就是螺旋形上升的開(kāi)發(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í)候也不早了,我們回去睡覺(jué)吧,過(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 閱讀(1803) 評(píng)論(6)  編輯 收藏 引用

評(píng)論

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

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

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

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

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

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

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

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

# re: 第一桶 從C到C++ 第九碗 陳老C演迭代開(kāi)發(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演迭代開(kāi)發(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   管理


<2025年10月>
2829301234
567891011
12131415161718
19202122232425
2627282930311
2345678

導(dǎo)航

統(tǒng)計(jì)

常用鏈接

留言簿(6)

隨筆檔案(21)

文章檔案(1)

搜索

最新評(píng)論

閱讀排行榜

評(píng)論排行榜

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            欧美刺激午夜性久久久久久久| 欧美日韩一区二区三区在线观看免| 亚洲经典三级| 久久久伊人欧美| 欧美亚洲视频一区二区| 午夜日韩在线| 蜜桃精品一区二区三区| 在线观看亚洲精品视频| 亚洲精品日韩激情在线电影| 亚洲三级免费电影| 亚洲欧美日韩国产一区二区| 久久9热精品视频| 亚洲第一黄色网| 99视频+国产日韩欧美| 午夜精品国产| 欧美成人精品一区| 国产精品午夜视频| 亚洲国产精品久久久久婷婷884| 9色国产精品| 久久狠狠婷婷| 亚洲毛片在线观看.| 欧美在线一二三区| 欧美日韩视频免费播放| 影音先锋中文字幕一区| 亚洲影院色无极综合| 蜜臀99久久精品久久久久久软件 | 一区二区三区四区五区视频 | 欧美视频一区二| 国产欧美精品日韩精品| 亚洲高清一区二| 欧美在线观看视频| 亚洲精品乱码久久久久久黑人| 欧美一区二区成人6969| 欧美片在线播放| 在线国产精品播放| 欧美伊人久久久久久午夜久久久久| 亚洲大片精品永久免费| 久久成人18免费观看| 国产精品―色哟哟| 99精品视频免费全部在线| 免费中文日韩| 久久精品国产亚洲aⅴ| 欧美日韩在线高清| 99综合视频| 亚洲福利免费| 麻豆国产精品777777在线| 国产一区二区高清不卡| 亚洲欧美伊人| 中日韩美女免费视频网站在线观看| 久热精品视频在线免费观看| 欧美v日韩v国产v| 日韩一级免费观看| 免费不卡视频| 亚洲二区三区四区| 久久综合九色综合网站| 午夜日本精品| 国产伦精品一区二区三区视频黑人 | 亚洲一区免费视频| 欧美亚洲第一区| 亚洲视频在线观看网站| 亚洲精品亚洲人成人网| 欧美日韩理论| 亚洲深夜福利| 亚洲免费电影在线观看| 欧美色欧美亚洲另类七区| 中日韩美女免费视频网址在线观看 | 欧美高清在线| 亚洲视频免费观看| 欧美一区二区视频97| 亚洲欧洲精品一区二区三区波多野1战4 | 久久久久高清| 欧美影视一区| 136国产福利精品导航| 欧美激情偷拍| 欧美精品麻豆| 午夜精品在线看| 久久se精品一区精品二区| 黄色成人在线| 最近看过的日韩成人| 亚洲免费影视| 国产美女一区二区| 久久久.com| 男人的天堂成人在线| 99re在线精品| 亚洲图片在线观看| 国产亚洲电影| 欧美成人xxx| 欧美人与性动交a欧美精品| 99爱精品视频| 欧美一级免费视频| 欧美日韩精品综合在线| 亚洲一区二区三区成人在线视频精品 | 美脚丝袜一区二区三区在线观看 | 老司机一区二区三区| 欧美精品久久一区二区| 久久高清一区| 欧美日韩国产综合久久| 久久久久久欧美| 欧美日韩情趣电影| 能在线观看的日韩av| 国产精品视频第一区| 亚洲高清自拍| 黄色成人精品网站| 亚洲永久字幕| 国产精品羞羞答答| 亚洲欧洲综合| 激情综合中文娱乐网| 一本大道久久a久久综合婷婷| 永久久久久久| 欧美亚洲一区二区在线观看| 99这里只有久久精品视频| 久久av在线| 亚洲欧美在线看| 欧美日韩国产色综合一二三四| 久久蜜桃香蕉精品一区二区三区| 欧美日韩视频在线| 亚洲精品久久久久| 亚洲精品1区2区| 久久这里只精品最新地址| 亚洲欧美另类中文字幕| 亚洲电影免费观看高清完整版在线 | 欧美综合国产| 欧美日韩午夜剧场| 欧美肥婆在线| 韩日成人在线| 亚洲一区二区伦理| 亚洲综合国产精品| 欧美日韩免费一区二区三区| 欧美国产三级| 亚洲黄色小视频| 欧美v日韩v国产v| 欧美国产日韩视频| 亚洲国产精彩中文乱码av在线播放| 久久精品国产欧美亚洲人人爽| 欧美中文在线视频| 国产亚洲成年网址在线观看| 性久久久久久久| 久久久久久久999精品视频| 国产综合在线看| 久久xxxx精品视频| 久久亚洲国产精品一区二区| 一区二区视频免费在线观看 | 久久免费国产精品1| 国产情侣久久| 久久精品日产第一区二区| 欧美1区3d| 欧美一区二区视频97| 欧美日韩精品系列| 久久er精品视频| 国产欧美日韩精品丝袜高跟鞋| 亚洲精品久久久久久久久久久久| 亚洲激情六月丁香| 欧美巨乳在线观看| 亚洲美洲欧洲综合国产一区| 亚洲一区亚洲二区| 国产精品视频久久| 久久er精品视频| 欧美国产综合| 亚洲视频一二| 国产在线观看一区| 另类天堂av| 亚洲精选久久| 久久精品国产第一区二区三区| 精品999在线播放| 欧美乱妇高清无乱码| 中文国产成人精品| 狼人天天伊人久久| 一本久久综合亚洲鲁鲁五月天 | 久久精品一二三区| 亚洲片在线观看| 久久精品一区二区国产| 亚洲精品欧美精品| 国产精品资源| 欧美精品一区二区视频| 香蕉国产精品偷在线观看不卡| 欧美激情久久久| 午夜视频在线观看一区| 亚洲国产高清一区二区三区| 国产精品女主播| 欧美大色视频| 性欧美暴力猛交69hd| 日韩视频在线观看免费| 久久野战av| 亚洲欧美日韩精品一区二区| 亚洲国产精品久久久久秋霞影院 | 亚洲高清123| 久久久久久久成人| 亚洲一区三区视频在线观看| 亚洲高清免费| 国产午夜精品在线观看| 欧美午夜精品理论片a级按摩| 美女精品网站| 午夜伦理片一区| 亚洲图片自拍偷拍| 99热这里只有成人精品国产| 欧美成人午夜剧场免费观看| 久久青青草原一区二区| 欧美在线播放视频| 欧美一区二区观看视频| 亚洲欧美日韩国产综合精品二区|