建立開發程式設計機制 (轉)

worldblog發表於2007-12-05
建立開發程式設計機制 (轉)[@more@]

 

建立機制

  對於而言,機制就是系統設計的;對於單個問題而言,機制就是解決問題的方法摸板。本文以例項來說明機制是如何建立和運作的。(機制也可以稱為,不過機制略多帶一些管理控制方面的內容)

  以前的程式設計過程中,雖然是按模組劃分,或按實現封裝,但從來沒有考慮過建立問題解決機制這麼一個概念。由於這次的專案大了,開發人員好幾個,不再是一個人的事情,所以,不得不考慮多人協同開發的方法。
  要想保證質量和進度地協同開發,將面對以下幾個困難:
  
1)開發者的水平和開發習慣不一樣,如何在統一思想的同時發揮個人能力;
2)系統的需求分析和設計不可能一步到位,調整系統設計後對每個開發者的影響很難估計,有時可能需要重做。如何降低調整系統設計對部分內容的影響程度;
3)開發人員的流動不可避免,技術資源的延續性如何得以保持僅僅靠詳細的文件,而不考慮程式自身的可讀性和可延續性是不夠的(讓新手瞭解每一個變數或的功能仍是很困難的)。
4)當時間進度已經臨近或者技術難度太大而要求簡化系統功能時,可能出現某些部分的開發已經要求另一部分必須完成的矛盾,如何避免此類騎虎難下的局面。
  此時,機制的建立就十分有必要了。這是一種程式設計標準的制定,這種標準不僅僅是變數、函式等命名規則,更多的是許多設計和編寫程式處理問題的法則和規定。
  對於系統而言,機制就是系統設計的程式;對於單個問題而言,機制就是解決問題的方法摸板。

  這裡有一個簡單而有效的辦法:
步驟一:統一開發群體的基本開發思想(即基本開發機制)
1)無論水平高低,每位成員必須以學習態度來到這裡共同開發
  對於任何一個未開發的專案,我們都是學生,即便是第二次開發此類專案,同樣有待學習。在一起開發的團體,首先要解決的是思路定式。開發和慣例並不是定論,創新的東西或許更有價值。所以,端正學習的態度來開發,無疑是重新審視已有技術和創新新的技術的保證。
2)不是在為自己開發程式,而是在為後人開發程式
  這句話的意思是:你開發的那部分程式就是一個完整的產品,它的就是後來的開發者。所以,你必須考慮你的產品如何簡單明瞭地交給其他開發者繼續開發或者擴充套件功能。
3)沒有抽象出問題解決公式不要寫程式碼
  如果是接到任務就不管3721地按需求寫出一堆程式碼,任務雖然是完成了,但這段程式碼也許跟著就廢了:增加或修改功能等於重做一次軟體。任務雖然是明確的,但任務中各項需求之間的關係和有可能更改需求的處理卻是不太明確的,這需要綜合分析。請看一個簡單的例子:
  CAD圖形設計中點、線、圓的繪製編輯和資料管理,要求有繪製、點選、刪除、移動、修改功能。以下設計是完全根據需求實現:
  #define dotID 1
  #define lineID 2
  #define CircleID 3

  struct TDot {
    long X,Y;
  };
  struct TLine {
    TDot start;
    TDot end;
  };
  struct TCircle {
    TDot center;
    long ;
  };
  struct TData {
    1=點 2=線 3=園
    long typeID;    別資料型別,1void *data;
  };
 class TPaper (
    TData *dataVal;
    void Draw(TData *data) {
      switch(data->typeID)
      {
      case 1:
        點
      case 2:
        線
      case 3:
        圓
      default:
      }
    }

    void Delete();
    void Move();
    bool (long x,long y);
    Update();
  };
  ......
  
  抽象各類圖形元素的設計:將點、線、圓的共性部分抽象成一個基類,使用虛擬函式,讓各自圖素各自解決畫法和操作。

  typedef MyDataType long

  struct RPoint {
    MyDataType X,Y;
  }
  class TLink {
    據指標鏈定義;
  }
  class TPaper : public TLink {
    virtual void Draw() {
      TPaper *temp;
      temp=FisrtData();
      while(temp不到結束) {
        temp->Draw();
        temp=temp->NextData;
      }
    }
    virtual void Delete();
    virtual void Move();
    virtual bool Select(long x,long y);
    virtual Update();
  };
  class TDot : public TPaper {
    RPoint crood;
    void Draw() {
      表示點。
crood.X,crood.Y畫一個十字交叉    }
    void Delete();
    void Move();
    bool Select(long x,long y);
    Update();
  }
  class TLine : public TPaper {
    RPoint start;
    RPoint end;
    void Draw() {
      (end.X,end.Y)畫一條線。
(start.X,start.Y)到    }
    void Delete();
    void Move();
    bool Select(long x,long y);
    Update();
  }
  class TCircle : public TPaper {
    RPoint center;
    MyLengthType radius;
    void Draw() {
      徑為radius畫一個圓。
(center.X,center.Y)為圓心,半    }
    void Delete();
    void Move();
    bool Select(long x,long y);
    Update();
  }
  ......

  兩段程式的區別:第一段,初期思路清晰,但,當增加畫圓弧或者二次曲線等時,程式的程式碼將改動的地方多,凡需要區分點、線、圓、以及其他的類別時,必須透過switch語句檢查當前應該顯示的型別資料。這樣導致增加功能或者變更畫法操作時,許多地方都要更改。第二段,初期設計時較難,經過分析設計後,所寫程式碼有很好的擴充套件性。增加一個圓弧,也僅僅只增加一個圓弧的類即可,而且許多操作都封裝到一個類中,不會對外部程式體沒有太大的影響。如果再增添一個序號產生器制,將新增內容註冊到TPaper中,這樣擴充套件功能更是易如反掌,並且還保證了系統的可靠性。
  如果覺得此方法的類佔用了太多的空間(虛擬函式在類例項中需要空間),可以建立一個基本結構:
  struct RDataBase {
    TPaper *type;
    RDataBase *nextData;
    void Add(RDataBase *);
    void Remove(RDataBase *);
  }
  struct RDot : public RDataBase{
    MyDataType X,Y;
  }
  struct RLine : public RDataBase {
    RPoint start,end;
  }
  struct RCircle : public RDataBase {
    RPoint center;
    MyDataType radius;
  }
  將圖素的資料分別實現為一個結構,將各自圖素類的一個例項指標置入一個TPaper *type指標變數中,Paper的Draw()實現為:
    virtual void Draw() {
      TPaper *temp;
      RDataBase *tempData;

      tempData=FisrtData();
      while(tempData不到結束) {
        temp=tempData->type;
        temp->Draw();
        tempData=tempData->nextData;
      }
    }
  經過一系列的問題抽象形成可確定的公式後,即便程式沒有實現太多的功能,我們可以認為它已經完成了90%的工作。因為,所有需要增加的內容已經被機制化,只要按照這樣一個明瞭的機制去實現圓弧、二次曲線、矩形框、多邊形、雙線等,不需要了解太多,其他開發者都可以很快加入它的工作。
  其實,物件導向的設計方法就是一種機制的建立,不過,物件導向偏重於型別的建立,注重封裝,而機制概念則不僅考慮類的建立,同時還考慮非類部分的建立,即使程式處理得非常沒有結構化,它的機制同樣存在。一個類封裝的不是非常好但機制確實很不錯的例子:
  上篇文章提到過的通用編輯器,為了實現編輯器摸板化,我們儘量將編輯器的共性設計在內部,而且讓擴充套件者獨立建立編輯器能夠識別的資料類(沒有任何派生繼承)。所以,採用了一系列的介面函式。其中,顯示介面函式更具有機制性:
  struct RData {     展者自己定義
    long ID;   零外的所有整數,由    
    char *data;  以是文字,也可以是圖形、表格或

    TFont *font;
  }
  當前需要顯示的內容
   *Get() 一個介面函式,返回  
   UserTextRect(text,X,Y) 介面函式,返回特顯內容的寬度  
  ShowText(long &X,long &Y)   示一行的內容  
  {  RData *text;
    text=(RData *)GetWord();
    while(text!=NULL) {
      if(text->ID==0)
        作為文字,內部顯示
        X+=MyTextOut(text,X,Y);  //

      else
        作為特顯,外部顯示
        X+=UserDataOut(text,X,Y); //

      text=(RData *)GetWord();
    }
  }
  
  這樣的機制,外部只需要在當前位置顯示應該顯示的內容,比如圖片。(當然,僅僅這樣的資料資訊還不夠,至少還要行高。但,這樣的機制是合理存在的。其他的資料管理機制不詳寫了)。在這個例子中,並不要求外部的擴充套件者有基類的限制,他僅僅提供介面函式即可。

步驟二:根據步驟一的基本開發機制,要求開發者提供兩份文件:程式說明文件和程式使用文件。程式說明和使用文件同時留給者,程式使用文件交給其他參與此部分程式的開發者。保證核心技術的保密同時提供可供擴充套件完善的機制。專案管理者還可以根據進度要求和資金實力,臨時招聘新的開發者,經過短期的培訓後,迅速加入開發行列,從而加快完成產品的進度。

步驟三:定期閱讀程式使用文件,檢查機制是否具有包容性、靈活性。檢查具體辦法:
1)機制能夠適應多少種情況,常見情況是否全包容,特殊情況考慮了多少?
2)如果考慮了特殊情況,那麼不存在特殊情況時機制是否有效、如何?
3)目前沒有考慮的情況,以後增加是否容易;如果不容易,是否簡單調整機制後能夠解決?
4)機制的理解是否容易,它的要求是否很多或者很難控制。
  經過步驟三的檢查,可最大程度地避免了系統更改(或簡化)後,對某些部分之間產生的邏輯矛盾。

步驟四:精益求精,機制的建立不能將就,必須經常性反思機制的機能合理程度,隨時修改完善。特別是才開始建立機制時,對機制要求更加細膩周全。

  下面一個CAD命令列處理機制的例子:

  定義:命令列只文字命令和提示資訊。選單、工具條、按鈕、熱鍵等均向命令列傳送命令和資料。命令列運作圖:(以畫圓為例)
  


  畫圓指令     命令列 
       等待輸入命令 
       | 
        輸入命令"c" 
      | 
 畫圓程式開始  〈===============  相應的畫圓程式 
傳送提示出選項  ================〉  等待選擇選項 
      | 
是圓心、半徑圓  〈===============  傳送給畫圓所選"r" 
傳送等待輸入半徑命令的項"r"  =======〉等待輸入半徑 
     | 
畫圓  〈===============  得到半徑 
畫圓結束  ================〉 等待輸入命令 
     

  
  互動式一問一答,將畫圓的所需引數一一輸入完後,畫圓。命令列從最開始就是等待輸入命令的狀態,當輸入命令後,命令列根據輸入命令呼叫相應的命令執行程式;在執行程式中,如果需要更多的引數資訊,將向命令列傳送選項或需要輸入的命令,控制權有交給了命令列;命令列等待選擇或輸入引數後,將所的結果返回到執行程式中,執行程式如果還需要資訊,繼續重複與命令列的互動傳送工作直到可以執行程式命令為止;執行完命令後,又將控制權交給命令列;命令列回到等待輸入命令。
  透過命令列這樣的操作,我們可以編寫一些命令批處理文字,當在命令列中貼上這些批處理文字後,命令列依次執行命令。命令列支援"" 預設輸入的控制等高階擴充套件功能。
  有了命令列的機制,實現其他圖素畫法變得如同文字操作一樣簡單。

  總之,如果設計出良好得機制,可以說就獲得有了可靠得核心程式。

夢郎


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-988701/,如需轉載,請註明出處,否則將追究法律責任。

相關文章