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

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

     “嗯,我估計(jì)那個(gè)地方以后會(huì)火!”老C一邊回味,一邊肯定的說道。
     “呵呵,不知道以后還會(huì)不會(huì)像現(xiàn)在的菜量一樣上……”小P有些懷疑。
     “哦,那我們就一直去,知道那一家菜量變小唄。”老C很有些主意,“剛吃完不要馬上坐下,我們走兩步吧。”
     “好啊。”小P應(yīng)道。
     兩個(gè)人從一村的天橋走回了東二……
     “來,我們來說說所謂的模塊是怎么回事。”老C喝了一口茶,又開始和小P聊起來,“你看,我們的程序按照邏輯可以分成幾個(gè)部分,分別是apple game這個(gè)游戲,child queue這個(gè)游戲的內(nèi)容以及queue這個(gè)抽象的數(shù)據(jù)結(jié)構(gòu)。在這里我們以名詞為中心,將對(duì)這些名詞的操作分別提煉為函數(shù),放到不同的文件里。這樣我們就 可以認(rèn)為有了三個(gè)邏輯單元……”老C一邊說,一邊在白板上畫了三個(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)。這是一種新的思考問題的方式,這種思考問題的方法強(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)了,只要你可以讀懂說明書,你自己就可以組裝電腦;但是收音機(jī)就不是這樣——雖然它的技術(shù)復(fù)雜度遠(yuǎn)比電腦小——如果你要去處理沒有被封裝過的模擬電 路,那你首先得本科畢業(yè)。”
     “唔……”小P點(diǎn)點(diǎn)頭。
     “還有一個(gè)好處是復(fù)用,如果我們可以將queue這個(gè)模塊提煉出來——使得它保持合理的接口——那么它就可以被用到其它地方,只要這個(gè)地方需要隊(duì)列這樣一 個(gè)數(shù)據(jù)結(jié)構(gòu)。”老C撓撓頭,“但是在這里我們想要提煉這個(gè)queue數(shù)據(jù)結(jié)構(gòu)還是有些困難的,因?yàn)樵谶@里它不是一個(gè)典型的queue,或者說它沒有表現(xiàn)出 queue的特質(zhì)。”
     “為什么呢?”小P問道。
     “因?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問道。
     “我們把具體需要的動(dòng)作用函數(shù)來表達(dá),這樣你就可以更清楚的看到我們需要什么樣的數(shù)據(jù)結(jié)構(gòu)了。”老C說道,“同樣我們還是需要偽代碼來幫忙。我們先試著不 要陷入為主的使用queue作為child queue的實(shí)現(xiàn),先用偽代碼將空白的地方添上,看看我們到底需要什么樣的數(shù)據(jù)結(jié)構(gòu)。”說著老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_;    
}

     “看來我們更加需要一個(gè)便于在任意地方插入與刪除元素的數(shù)據(jù)結(jié)構(gòu),而不是便于在頭和尾部插入與刪除元素的數(shù)據(jù)結(jié)構(gòu)。”老C道,“首先我們了解了需求,現(xiàn)在我們看如何將這些需求組織成為我們代碼的單元模塊。”老C搓搓手,“這樣吧,我來寫,你來看。”說著他開始改寫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需知道也不可能知道list模塊是怎么樣初始化的。其它的接口也一樣,它們都是根據(jù)具體的應(yīng)用環(huán)境需求提出來的。”老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í)在程序中用宏定義出來的,所以在初始化的時(shí)候我使用斷言確保初始化程序可以在正常的條件下進(jìn)行。但是如果linked list是由外部輸入,比如鍵盤輸入得到,那么我們最好使用一個(gè)if()判斷來處理用戶輸入的異常情況……總之看你認(rèn)為這些反常的情況是出現(xiàn)在程序內(nèi)部還 是外部——這些我以后還會(huì)談到的。關(guān)于斷言,你可以在網(wǎng)上搜索一下。”老C解釋。
     “好的……又是一個(gè)需要經(jīng)驗(yàn)積累的東西嗎?”小P問。
     “呵呵,可以這樣說,”老C點(diǎn)點(diǎn)頭,“你先大概看看代碼的結(jié)構(gòu),不必糾纏于細(xì)節(jié),我們?cè)賮砜纯磍inked list的具體實(shí)現(xiàn)。”說完老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í)候比較好處理;注意在這里我們使用了左閉右開定義域,就是說使用了[first, last)的形式定義鏈表的定義域,這樣在計(jì)算長(zhǎng)度和循環(huán)迭代上都很方便,關(guān)于這方面的內(nèi)容我們以后還會(huì)討論到……”老C撓 撓頭,接著說道,“注意在刪除內(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開始在屏幕上翻來翻去,滾動(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)過我們這個(gè)項(xiàng)目測(cè)試過的,完全可以用在其他地方以減少開發(fā)和測(cè)試的工作量。”老C習(xí)慣性的總結(jié)道。
     “但是……等等……好像list模塊最多只能用一次,因?yàn)闆]有辦法在一個(gè)工程中定義兩個(gè)LIST_CONTENT類型……”小P突然注意到什么,很是興奮的分析道。
     “呵呵,這個(gè)是下一步的工作了,你先看看這一版的實(shí)現(xiàn)是否正確……”老C開始有些佩服小P的觀察力了。
     “嗯,我來看看程序是否工作正常……”小P修改了applegame.hCHILDREN_NUMKICK_OUT_NUM 宏的定義,觀察了調(diào)試信息的輸出,“嗯,我認(rèn)為沒有什么問題。”他點(diǎn)頭說道。
     “注意值為1的邊界情況和0的非正常情況。”老C提醒。
     “知道了,”小P應(yīng)道,又嘗試了一些邊界情況和非正常情況,“嗯,程序的執(zhí)行沒有什么問題,在0值的情況下會(huì)出現(xiàn)runtime錯(cuò)誤。”
     “好啦,我們的C編碼活動(dòng)告一段落啦。”老C說完將所有文件拷貝到AppleGame_V1.02,然后將AppleGame_V1.02命名為AppleGame_V2.0。“我們已經(jīng)進(jìn)行了簡(jiǎn)單的開發(fā)活動(dòng),讓我們來總結(jié)總結(jié)……”
     “是么?有哪些需要總結(jié)的地方?”小P摸摸頭,問道。
     “首先是思想和原理性的東西,其次是方法上的東西。”老C道。
     “?槑”小P有些莫名其妙。
     “呵呵,我來說你來寫吧……把白板擦干凈……”老C揉揉手指,決定偷懶一下。
     “好!”
     老C接過彩筆,在白板中間從上到下畫了一道線,左邊寫上思想,右邊寫上方法。“你先寫寫思想上的東西吧,”他喝了一口水,“思想是最重要的,我們需要通過 學(xué)習(xí)語(yǔ)言來學(xué)習(xí)思想——只要學(xué)會(huì)了編程的思想,那么你再學(xué)習(xí)其他任何語(yǔ)言都會(huì)很快——要深入語(yǔ)言去學(xué)習(xí),而不是只是使用語(yǔ)言。首先我們的第一個(gè)經(jīng)驗(yàn)是,以 數(shù)據(jù)為中心思考問題,而不是以活動(dòng)為中心思考問題。”
     “嗯,好像沒有什么問題,如果我們以數(shù)據(jù)為中心思考問題,那么總會(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ù)為中心思考問題的解決之道。
2. 將對(duì)同類數(shù)據(jù)的操作放在相同的編譯單元中。

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

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

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

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

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

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

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

方法:
1. 首先關(guān)注整體,而不是細(xì)節(jié)。
2. 先考慮測(cè)試,更先于編碼。
3. 先用偽代碼編程,以體現(xiàn)程序的意圖,再用具體代碼完善之。
4. 迅速構(gòu)建可編譯通過的,并且可執(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問道。
     “因?yàn)槲覀円呀?jīng)將問題分解為幾個(gè)部分了,對(duì)每一個(gè)部分反復(fù)運(yùn)用我們以上總結(jié)的四個(gè)經(jīng)驗(yàn),這樣我們就得到了更細(xì)小的模塊,遞歸下去,直到每個(gè)模塊的規(guī)模都在我們的掌控之中,我們就解決問題了。”老C回答到。
     “是啊是啊,這個(gè)很像分而治之的思想。”小P道。
     “沒錯(cuò),你總結(jié)的很到位,這就是分而治之思想的一個(gè)運(yùn)用而已。”老C開始有些佩服小P的總結(jié)能力和聯(lián)想能力了,“還有一些方法需要總結(jié),比如說要在代碼環(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)出來的一些更加具體的操作,自然被隱藏起來成為linked list內(nèi)部的static函數(shù)。不過在我們的代碼中,還沒有這樣的被隱藏起來的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)建可編譯通過的,并且可執(zhí)行的框架程序,使用測(cè)試代碼測(cè)試之。
5. 將以上方法反復(fù)應(yīng)用于子模塊,直到問題被解決。
6. 在上下文環(huán)境中提取對(duì)其他模塊的需求。
7. 先寫.h文件,后寫.c文件。

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

方法:
1. 首先關(guān)注整體,而不是細(xì)節(jié)。
2. 先考慮測(cè)試,更先于編碼。
3. 先用偽代碼編程,以體現(xiàn)程序的意圖,再用具體代碼完善之。
4. 迅速構(gòu)建可編譯通過的,并且可執(zhí)行的框架程序,使用測(cè)試代碼測(cè)試之。
5. 將以上方法反復(fù)應(yīng)用于子模塊,直到問題被解決。
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ù) 來上幾次才找到的——除非你所在的團(tuán)隊(duì)對(duì)問題的領(lǐng)域特別熟悉,對(duì)需求了解相當(dāng)透徹。”老C總結(jié),“好啦,時(shí)候也不早了,我們回去睡覺吧,過幾天我們來看看 用C++如何解決這個(gè)問題。”
     “好吧,你先回去吧,我把白板上的東西抄回去,再對(duì)照著我們寫過的代碼仔細(xì)琢磨琢磨。”小P道。
     “哈哈,算了算了,明天再搞吧,今天已經(jīng)晚了。勞逸結(jié)合才是正道,不如你今晚和我再打打魔獸吧……”老C笑道。
     “……也好……看我再給你支幾招……”小P笑道。
     “呵呵,那就趕快回去吧,我的手有些癢癢了……”老C拉起小P,飛快的走出教研室。

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

posted on 2009-02-18 23:49 Anderson 閱讀(1798) 評(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 崔友志

是啊,樓主的文章無論從寫作風(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ù)前面增加了一些說明性的文字,希望不熟悉STL的xdjm們看起來可以更清楚一些……  回復(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   博問   Chat2DB   管理


<2009年2月>
25262728293031
1234567
891011121314
15161718192021
22232425262728
1234567

導(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>
            欧美国产精品va在线观看| 一区二区三区日韩欧美| 久久久不卡网国产精品一区| 亚洲视频在线一区| 一区二区高清视频| 欧美电影打屁股sp| 久久婷婷综合激情| 亚洲国产午夜| 最新日韩在线视频| 亚洲愉拍自拍另类高清精品| 久久久99久久精品女同性| 欧美国产精品va在线观看| 国产精品美女在线观看| 国产精品女人网站| 亚洲国产精品久久久久久女王| 在线观看成人网| 妖精视频成人观看www| 午夜精品电影| 欧美va亚洲va香蕉在线| 一本色道久久综合亚洲精品高清 | 欧美顶级艳妇交换群宴| 亚洲日本欧美天堂| 亚洲免费网址| 美女脱光内衣内裤视频久久网站| 欧美激情在线观看| 国产一区二区三区网站| 亚洲伦理精品| 久久久久国产精品午夜一区| 亚洲国产精品成人久久综合一区| 一区二区欧美亚洲| 久久深夜福利免费观看| 国产精品毛片a∨一区二区三区| 一区二区三区我不卡| 午夜精品剧场| aa级大片欧美三级| 欧美成人精品一区二区三区| 国产欧美一区二区三区国产幕精品| 亚洲国产小视频在线观看| 欧美专区18| 亚洲性视频h| 欧美日韩综合| 亚洲国产精品嫩草影院| 久久久久久久综合狠狠综合| 一区二区高清视频| 欧美a级一区二区| 久久久天天操| 日韩亚洲不卡在线| 国产主播精品在线| 亚洲欧美综合精品久久成人| 亚洲欧洲免费视频| 老司机精品福利视频| 激情久久综艺| 久久午夜视频| 久久国产精品免费一区| 国产午夜精品一区二区三区视频| 亚洲欧美变态国产另类| 夜夜嗨一区二区三区| 欧美日韩免费观看一区三区| 日韩手机在线导航| 亚洲国产婷婷香蕉久久久久久99| 久久综合导航| 亚洲人成网站色ww在线 | 亚洲国产精品成人综合色在线婷婷| 亚洲欧美日韩中文在线制服| 欧美小视频在线| 午夜精品美女久久久久av福利| 亚洲视频导航| 国产视频一区在线观看一区免费| 久久成人免费日本黄色| 久久精品国产69国产精品亚洲 | 欧美高清视频一区二区| 一区二区三区高清不卡| 99国产精品99久久久久久粉嫩| 欧美日韩亚洲一区二区三区在线观看 | 91久久精品国产91久久| 亚洲高清123| 欧美日韩国产123区| 久久久精品国产99久久精品芒果| 国产精品色婷婷久久58| 欧美中文字幕不卡| 久久在精品线影院精品国产| 亚洲免费激情| 亚洲欧美国产va在线影院| 国产亚洲毛片| 亚洲国产日韩一区| 国产精品久久久久久一区二区三区| 欧美一区免费| 欧美+亚洲+精品+三区| 亚洲无线视频| 久久久精彩视频| 在线午夜精品自拍| 欧美专区福利在线| 一区二区三区久久久| 欧美一区二区精品在线| 亚洲韩国青草视频| 亚洲一区二区在线视频 | 欧美日韩国产一区二区| 午夜一级久久| 男女精品网站| 欧美日韩在线观看视频| 久久精品夜色噜噜亚洲a∨| 免费成人高清视频| 欧美在线免费播放| 欧美精品情趣视频| 久久精品五月| 欧美日韩国产探花| 欧美ab在线视频| 国产乱码精品一区二区三| 欧美大色视频| 国产精品试看| 一本久久精品一区二区| 亚洲高清av| 欧美在线高清| 欧美一二区视频| 欧美日本不卡| 欧美aⅴ99久久黑人专区| 国产精品视频专区| 亚洲每日在线| 亚洲精品无人区| 久久久精品网| 久久国产精品久久久久久| 欧美日韩视频在线一区二区观看视频 | 亚洲一品av免费观看| 久久一综合视频| 久久久久亚洲综合| 国产欧美婷婷中文| 亚洲免费婷婷| 欧美一区二区三区男人的天堂| 欧美日韩一区在线| 日韩视频一区二区在线观看| 日韩视频中文| 欧美精品国产精品| 亚洲人久久久| 亚洲图色在线| 欧美日韩综合不卡| 亚洲网站在线| 欧美一区二区三区视频免费播放| 欧美四级在线观看| 亚洲一级在线观看| 欧美一区二区视频网站| 国产麻豆成人精品| 欧美综合77777色婷婷| 看欧美日韩国产| 亚洲精品乱码| 欧美日韩综合视频网址| 99在线精品观看| 午夜精品www| 国产亚洲精品久久久久婷婷瑜伽 | 鲁大师成人一区二区三区| 亚洲大胆人体在线| 欧美成人精品1314www| 最新国产拍偷乱拍精品| 亚洲尤物视频网| 国产一区二区高清不卡| 久久综合国产精品| 亚洲高清久久| 亚洲一区二区三区精品视频| 国产精品试看| 久久综合电影| 一区二区三区.www| 久久久亚洲精品一区二区三区 | 欧美成人在线网站| 夜夜精品视频| 国产精品区一区二区三区| 欧美资源在线观看| 亚洲精品123区| 欧美在线观看网址综合| 狠狠狠色丁香婷婷综合久久五月| 女女同性精品视频| 亚洲视频图片小说| 久久这里有精品15一区二区三区| 91久久精品一区二区别| 国产精品v日韩精品v欧美精品网站| 性娇小13――14欧美| 欧美激情2020午夜免费观看| 亚洲欧美在线x视频| 亚洲国产精品欧美一二99| 国产精品久久久久久久久久妞妞 | 久久在线精品| 亚洲夜间福利| 亚洲黄色在线观看| 国产色婷婷国产综合在线理论片a| 麻豆精品在线观看| 午夜精品一区二区三区在线视| 亚洲电影有码| 久久嫩草精品久久久久| 一区二区三区国产精品| 在线观看精品一区| 国产日韩欧美视频| 国产精品xxxav免费视频| 欧美aaaaaaaa牛牛影院| 久久国产精品免费一区| 亚洲一级在线观看| 日韩一区二区精品在线观看| 欧美激情免费在线| 麻豆国产精品777777在线| 欧美在线观看网址综合| 亚洲一级黄色片| 一本久道久久综合狠狠爱| 亚洲成人在线视频播放|