2010年3月31日

          1.1 BUILDER生成器

          1、 意圖

          將一個復雜對象的構建和它的表示分離,使得同樣的構建過程可以創建不同的表示。

          構建是通過對生成器抽象接口的調用實現的。而構建出來的產品的類型(表示),是由具體的進行構建的生成器的子類的實例來決定的。這樣導向器只需要調用生成器的抽象接口生成產品,而具體生成什么樣的產品,則是根據導向器配置的具體的生成器實例相關。

          終于理解了它是如何把復雜對象的構建和它的表示分離的,也理解了為什么同樣的構建過程。

          2、 動機

          3、 適用性

          當創建復雜對象的算法應該獨立于該對象的組成以及他們的裝配方式時——對象的組成及裝配方式是生成器的抽象接口來表示的。而對象的創建的算法是有生成器的子類實現的。

          當構造過程必須允許被構造的對象有不同的表示時——也就是,同樣的構造過程,可以生成不同的產品。這里是通過配置導向器的生成器的子類實例來實現的。

          4、 結構

          wps_clip_image-372

          5、 參與者

          l Builder:為創建一個product對象的各個部件指定抽象接口。這些部件最終構成了product,而對這些抽象接口的調用,則是裝配product的操作,調用的次數,入參等不同,則最終生成的product也不同。product本身的構造過程可能會非常復雜。但是,這些復雜度對Director(導向者)是隱藏的。這些抽象的接口描述了產品的組成以及裝配方式。

          l ConcreteBuilder:實現Builder的接口以構造和裝配產品的各個部件;定義并明確它所創建的表示;提供一個檢索產品的接口。這個類實現了Builder的抽象接口,從而可以創建不同的表示,但是組成和裝配過程還是一樣的。

          l Director:構造一個使用Builder抽象接口的對象。更對象根據Builder的接口來裝配并生產產品。

          l Product:表示被構造的復雜的對象。ConcreteBuilder創建該產品的內部表示,并定義它的裝配過程;包含定義組成部件的類,包括將這些部件裝配成最終產品的接口。

          6、 協作

          l 客戶創建Director對象,并用它想要的Builder對象進行配置。

          l 一旦產品不僅被生成,導向器就會通知生成器。

          l 生成器處理導向器請求,并且將部件添加到該產品中。

          l 客戶從生成器中檢索產品。

          wps_clip_image-911

          7、 效果

          l 它使你可以改變一個產品的內部表示。Builder提供給Director的抽象接口可以使生成器隱藏這個產品的表示和內部結構。它同時也隱藏了該產品時如何裝配的。因為產品時通過抽象接口構造的,你改變該產品的內部表示時,所要做的只是定義一個新的生成器。

          l 它將構造代碼和表示代碼分開。Builder模式通過封裝一個復雜對象的創建和表示方式提高了對象的模塊性。每個ConcreteBuilder包含了創建和裝配一個特定產品的所有代碼。

          l 它可以使你對構造過程進行更精細的控制。

          8、 實現

          l 通常一個抽象的Builder類為導向者可能要求創建的每一個構件定義一個操作。這些操作缺省什么也不做。一個ConcreteBuilder類對它有興趣創建的構建重定義這些操作。

          l 裝配和構造接口:一般構造請求的結果只是被添加到產品中,特殊情況下,需要返回給導向器。

          l 產品沒有抽象類:一般他們沒有公共的部分。如果有也可以設置一個抽象類。

          l Builder中缺省的方法為空。

          9、 代碼示例

          class MazeBuilder {

          public:

              virtual void BuildMaze() { }//部件的構造方法

              virtual void BuildRoom(int room) { }

              virtual void BuildDoor(int roomFrom, int roomTo) { }

              virtual Maze* GetMaze() { return 0; }

          protected:

              MazeBuilder();

          };//MazeBuilder是生成器

          class StandardMazeBuilder : public MazeBuilder {

          public:

              StandardMazeBuilder();

          /*

          */

              virtual void BuildMaze();

              virtual void BuildRoom(int);

              virtual void BuildDoor(int, int);

          /*

          */

              virtual Maze* GetMaze();

          private:

              Direction CommonWall(Room*, Room*);

              Maze* _currentMaze;

          };//StandardMazeBuilder是ConcreteBuilder,提供部件的具體構造代碼

          Maze* MazeGame::CreateMaze (MazeBuilder& builder) {

              builder.BuildMaze();

              builder.BuildRoom(1);

              builder.BuildRoom(2);

              builder.BuildDoor(1, 2);

              return builder.GetMaze();

          }//CreateMaze是導向器,調用生成器的抽象接口完成產品的構造過程。

          //下面代碼描述產品的構造過程

          Maze* maze;//最終的產品

          MazeGame game;//Director,導航者

          StandardMazeBuilder builder;//ConcreteBuilder,實際的構造類

          game.CreateMaze(builder);//開始裝配

          maze = builder.GetMaze();//獲取裝配后的產品

          posted @ 2010-03-31 21:07 常高偉 閱讀(240) | 評論 (0)編輯 收藏


          2010年3月3日

          1.1 ABSTRACT FACTORY 抽象工廠

          1、 意圖

          提供一個創建一系列相關或相互依賴對象的接口,而無需指定他們具體的類。

          2、 動機

          “客戶僅與抽象定義的接口交互,而不使用特定的具體類的接口。”

          這里的主要的思想是封裝對象的創建的過程。客戶端可以不需要知道具體要創建那些對象,而只需要知道創建某一系列的對象所用到的“工廠對象”即可。

          3、 適用性

          一個系統要獨立于它的產品的創建、組合和表示時。 

          一個系統要由多個產品系列中的一個來配置時。 

          當你要強調一系列相關的產品對象的設計以便進行聯合使用時。 

          當你提供一個產品類庫,而只想顯示它們的接口而不是實現時。 

          4、 結構

          wps_clip_image-278

          5、 參與者

          AbstractFactory:創建一系列對象的抽象類。

          ConcreteFactory:實現具體創建產品對象的操作。

          AbstractProduct:為一類產品對象聲明一個接口。

          ConcreteProduct:定義一個被相應的具體工廠創建的對象;實現AbstractProduct接口。

          Client:僅使用AbstractFactory和AbstractProduct類聲明的接口。

          6、 協作

          在運行時刻,創建一個ConcreteFactory實例,它創建具有特定實現的對象。為創建不同的對象,客戶應使用不同的具體工廠。

          AbstractFactory將具體對象的創建延遲到它的子類ConcreteFactory中。

          7、 效果

          1) 它分離了具體的類:一個工廠封裝創建產品的責任和過程,它將客戶和類的實現分離。客戶通過抽象接口操作實例。產品的類名也在具體工廠實現中分離,他們不出現在客戶代碼中。

          2) 它使得易于交互產品系列。

          3) 它有利于產品的一致性。

          4) 難于支持新的種類。

          posted @ 2010-03-03 20:27 常高偉 閱讀(190) | 評論 (0)編輯 收藏

          1.1 設計模式怎樣解決設計問題

          1.1.1 尋找合適的對象

          面向對象設計最困難的部分是將系統分解為對象的集合。

          設計的許多對象來源于現實世界的分析模型,這里和領域驅動設計有點關聯。分析所得到的類,很多事現實中并不存在的類。這是抽象的結果。設計中的抽象對于產生靈活的設計至關重要。就像我設計的一個流程調度模型。

          1.1.2 決定對象的粒度

          記筆記可以讓我達到沉流的狀態。

          1.1.3 指定對象接口
          1.1.4 描述對象實現

          OMT表示法:

          1、 對象:最上面的黑體表示類名,下面依次是操作,數據。

          wps_clip_image-219

          2、 實例化:虛線箭頭表示一個類實例化另外一個對象。

          wps_clip_image-245

          3、 繼承:豎線和三角表示繼承關系。

          wps_clip_image-263

          4、 抽象類:類名以黑體斜體表示,操作也用斜體表示。

          5、 引用

          wps_clip_image-293

          箭頭加黑點表示一個類引用另外一個類。

          重點:

          1、 類的繼承和接口繼承的比較

          對象的類和對象的類型的區別:

          對象的類定義了對象是怎樣實現的,同時也定義了對象內部狀態和操作的實現。對象的類型只與它的接口有關。一個對象可以由多個類型(支持多個接口),不同類的對象可以有相同的類型。

          類和類型緊密相連,類定義了對象的操作,也定義了對象的類型。

          類的繼承和接口的繼承的差別:

          c++中接口繼承接近于公有繼承純抽象類。純實現繼承或純類繼承接近于私有繼承。

          2、 對接口編程,而不是對實現編程——面向對象設計的第一個原則

          1.1.5 運用復用機制

          1、 繼承和組合的比較

          繼承是一種白箱復用,父類的內部細節對子類可見。

          對象組合彼此不知道對方內部細節,成為黑箱復用。

          繼承的優缺點:

          1) 子類可以直接重定義父類的操作。

          2) 編譯時刻決定了,無法在運行期間更改。

          3) 子類要知道父類的實現細節,這樣就部分破壞了封裝性。子類和父類依賴過于緊密,父類的某些變化必然導致子類的變化。開發過程中遇到過類似的問題。這種依賴,限制了靈活性以及復用性。比如,服務體系中經常出現這樣的問題,導致代碼拷貝。

          組合(通過獲得對象的引用而在運行時刻動態的定義)的優缺點:

          1) 對象間通過接口彼此交互。

          2) 對象只能通過接口訪問,不要也不能知道對方細節,這樣不會破壞封裝性。

          3) 運行時刻可以使用另外一個對象替換這個對象,提高了靈活性。

          4) 對象的實現基于接口編寫,所以實現上存在較少的依賴關系。

          5) 優先使用組合有助于保持每個類被封裝,并被集中在單個任務上,提高整體內聚性。類和類的層次都維持一個較小的規模,

          6) 基于對象組合的設計會有更多的對象(而又較少的類),且系統的行為依賴于對象間的關系而不是定義在某個類的內部。

          理想的情況下,應該通過組合原有構件實現新的功能,而不是創建新的構件。

          面向對象設計的第二個原則:優先使用對象組合,而不是類繼承。

          2、 委托

          委托時一種組合方法,它是組合具有與繼承同樣的能力。

          委托的主要優點在于它便于在運行時刻組合對象操作,以及更改操作的組合方式。它是軟件更加的靈活。

          和其他的技術方案相同,它也存在不足之處:增加了軟件的復雜度——動態的,高度參數化的軟件比靜態的軟件更難于理解。

          3、 繼承和參數化類型的比較

          1.1.6 關聯運行時刻的結構和編譯時刻的結構
          1.1.7 設計應支持變化

          設計應該支持變化——所說的是,一個設計方案,對變化要有一定的適應性,即封裝變化。

          變化是導致重新設計的原因。設計要對一定范圍內的變化友好。

          4、 

          對于程序的分層設計,對于處于同一分層的模塊,對外應保持一定的抽象,并且,使用同種類型的通信協議。

          posted @ 2010-03-03 20:26 常高偉 閱讀(435) | 評論 (0)編輯 收藏

          1.1 變量存儲域

          1.1.1 一個示例

          pang123hui首先提供了一個網上流傳的學習代碼示例:

          int a = 0; //全局區 

          void main() 

          {

          int b; //棧 

          char s[] = “abc”; //s在棧,abc在文字常量區 

          char *p1,*p2; //棧 

          char *p3 = "123456"; //123456在常量區,p3在棧上 

          static int c =0; //全局區 

          p1 = (char *)malloc(10); //p1在棧,分配的10字節在堆 

          p2 = (char *)malloc(20); //p2在棧,分配的20字節在堆 

          strcpy(p1, "123456"); //123456放在常量區 

          }

          這個代碼示例中出現了“全局區”,“棧”,“文字常量區”,“堆”等詞語。為了統一,我們使用《C專家編程》中的說法:堆棧段,BSS段,數據段,文本段。

          各個段的作用如下:

          1、 文本段:包含程序的指令,它在程序的執行過程中一般不會改變。

          2、 數據段:包含了經過初始化的全局變量和靜態變量,以及他們的值。

          3、 BSS段:包含未經初始化的全局變量和靜態變量。

          4、 堆棧段:包含了函數內部聲明的局部變量。

          當然,上面段的作用不僅于此,具體的作用會在下面的知識點中介紹。

          1.1.2 通過代碼測試變量的存儲位置

          Linux下可以通過系統命令“size”查看可以執行程序各個段的大小。但是,可執行程序中的段結構和運行中程序在內存中的段結構并不完全相同,但是有一定的映射關系。具體如下圖所示(圖片信息來自《C專家編程》):

          wps_clip_image-696

          下面通過代碼示例和“size”來研究變量的存儲區域。

          test.c

          int main()

          {

          return 1;

          }

          編譯,并且查看可執行程序各個段的大小:

          wps_clip_image-779

          更改test.c:

          int g_data;

          int main()

          {

          return 1;

          }

          編譯,并且查看可執行程序各個段的大小:

          wps_clip_image-849

          可以發現,文本段,數據段都沒有發送變化,而BSS段增加了4個字節。

          結論1:未初始化的全局變量保存在BSS段中

          繼續:

          int g_data = 1;

          int main()

          {

          return 1;

          }

          編譯:

          wps_clip_image-958

          可以發現,BSS段和文本段相同,而數據段增加了4個字節。

          結論2:經過初始化的全局變量保存在數據段中

          繼續:

          int main()

          {

          static int g_data;

          return 1;

          }

          編譯:

          wps_clip_image-1066

          可以發現,文本段,數據段都沒有發送變化,而BSS段增加了4個字節。

          結論3:未初始化的靜態變量保存在BSS段中

          繼續:

          int main()

          {

          static int g_data = 1;

          return 1;

          }

          編譯:

          wps_clip_image-1183

          可以發現,BSS段和文本段相同,而數據段增加了4個字節。

          結論4:經過初始化的靜態變量保存在數據段中

          繼續:

          int main()

          {

          int i_data = 1;

          return 1;

          }

          編譯:

          wps_clip_image-1288

          可以發現,BSS段和和數據段相同,而文本段增加了16個字節。局部變量會在執行的時候在堆棧段中生成,函數執行完畢后釋放。

          結論5:函數內部聲明的局部變量保存在堆棧段中

          繼續:

          const int g_data = 1;

          int main()

          {

          return 1;

          }

          編譯:

          wps_clip_image-1430

          把全局變量定義為“const”后,也許你會感到奇怪,怎么BSS段和數據段都沒有發生變化,而文本段卻增加了4個字節。

          結論6:const修飾的全局變量保存在文本段中

          那么,const的局部變量?

          繼續:

          int main()

          {

          const int i_data = 1;

          return 1;

          }

          編譯:

          wps_clip_image-1587

          結論7:const修飾的局部變量保存在堆棧段中

          繼續:

          char *pstr = "";

          int main()

          {

          return 1;

          }

          編譯:

          wps_clip_image-1666

          在做一下更改:

          char *pstr = "123456789";

          int main()

          {

          return 1;

          }

          編譯:

          wps_clip_image-1733

          可以發現,前后數據段和BSS段大小均未發生變化,而文本段增加了9個字節。

          結論8:字符串常量保存在文本段中

          1.1.3 結論

          1、 經過初始化的全局變量和靜態變量保存在數據段中。

          2、 未經初始化的全局變量和靜態變量保存在BSS段。

          3、 函數內部聲明的局部變量保存在堆棧段中。

          4、 const修飾的全局變量保存在文本段中,const修飾的局部變量保存在堆棧段中。

          5、 字符串常量保存在文本段中。

          1.1.4 擴展閱讀

          《C專家編程》第6章——詳細介紹各個段的作用。

          posted @ 2010-03-03 02:38 常高偉 閱讀(295) | 評論 (0)編輯 收藏


          2009年10月14日

          讀S計劃的理念

          自助、互助,共同進步!

           

          讀S計劃的初衷

          現在有很多學習方式,搜索引擎、論壇、博客,qq群等等,那么我這樣的計劃還有存在的必要么?這個計劃的獨特之處在哪里?

          讀S計劃的獨特之處不在于其學習內容和方式,學習的內容我們可以根據實際情況調整,可以由多個人同時引導多個學習方向,這些都不是程式化的,也不是重點。

          讀S計劃的獨特之處在于其理念:致力于形成一種有計劃、有組織,強調互動、互助和共同進步的學習模式和氛圍。讀S計劃不是一個單純的興趣小組,我們會按照計劃不斷的切換話題。讀S計劃也不是提問的好地方,在每個階段,我們都有限定的討論范圍,所以無關的問題很容易被忽略。讀S計劃希望多數人以積極的心態參與進來,更多的討論、研究和貢獻您的想法,而不是被動的接受和看,我們不希望將學習計劃做成課堂模式,我們希望做成項目的模式 。讀S計劃致力于將信息以信息本身進行聚合,而不是以人進行聚合,因為學習計劃在不斷推進,所以不存在永遠的權威人士,但知識本身是客觀、中立的、權威的。

           

          共建新型技術社區

          建立新型的、關系緊密的、以知識體系為軸、強調經驗積累與分享的的技術社區。

           

          更深入了解此計劃,可以閱讀:

          《技術族譜 之 讀S計劃:讓我們一起快樂學習》

          《準備啟動一個開源項目 - 技術族譜 - 先期利用Goolge云計算平臺》

          《關于“讀S計劃”的聊天記錄:統一理念,確定目前的工作》

          《授人以“魚”,不如授人以“漁”,放棄一個目標,設定另一個目標》

          期待你加入我們的群組:http://hi.csdn.net/space-mtag-tagid-37.html

          posted @ 2009-10-14 21:43 常高偉 閱讀(206) | 評論 (0)編輯 收藏


          2009年10月12日

          上面一篇文章大致描述了一下插件開發框架整體結構。這篇描述一下核心層的設計和實現。

          至于核心層的設計,我想借鑒 一下微內核的思想。核心層只負責實現下面幾個功能:

          1、 插件的加載,檢測,初始化。

          2、 服務的注冊。

          3、 服務的調用。

          4、 服務的管理。

          插件的加載,檢測,初始化

          插件的加載利用linux共享庫的動態加載技術。具體的方法可以看一下IBM網站的一篇資料《Linux 動態庫剖析》

          服務的注冊

          服務的注冊與調用采用表驅動的方法。核心層中維護一個服務注冊表。

          //插件間交互消息類型
          typedef enum __Service_Type
          {
              Service_Max,
          }Service_Type;
          //插件用于和其他插件通信接口函數,由插件提供。
          typedef PRsp_Ele_Stream (*PF_Invoke_Service_Func)(PReq_Ele_Stream pele_str);
          //驅動表
          typedef PF_Invoke_Service_Func Service_Drive_Table[Service_Max];

          驅動表是一個數組,下標為插件間交互消息類型,成員為插件提供的接收的消息處理函數,由插件初始化的時候,調用插件框架的的注冊函數注冊到驅動表。

          插件的初始化實現為:
          //插件用于注冊處理的消息類型的函數,由插件框架提供。
          typedef RET_RESULT (*PF_Service_Register_Func)(Service_Type service_type);
          //插件用于和其他插件通信接口函數,由插件框架提供。
          typedef PRsp_Ele_Stream (*PF_Invoke_Service_Func)(PReq_Ele_Stream pele_str);
          //插件回復響應函數。插件收到異步請求后,處理完成后,發送響應消息給請求的插件。由插件框架提供
          typedef void (*PF_Send_Response_Func)(PRsp_Ele_Stream pele_str);
          //初始化插件信息
          typedef struct Plugin_Init_St
          {
              PF_Service_Register_Func register_func;//服務注冊函數,要注冊一系列的枚舉值。插件可以處理的服務枚舉值
              PF_Invoke_Service_Func invoke_serv_func;//和其他組件交互時,調用的用于和其他組件交互的函數。發送請求消息。
              PF_Send_Response_Func send_rsp_func;//再設計一個回復響應消息的接口。收到異步請求后,處理完畢后通知請求模塊處理結果。
          } Plugin_Init_St, *PPlugin_Init_St;
          //初始化插件函數,類似于構造函數。由插件提供,供插件框架加載插件時初始化插件使用。
          void PF_Init_Plugin(PPlugin_Init_St pinit_info);

                插件在函數PF_Init_Plugin中調用函數register_func來注冊插件要處理的消息類型。

          服務的調用
          //信元結構體
          typedef struct Ele_St
          {
              Ele_Tag tag;
              Ele_Length len;
              Ele_Value  value;
              PEle_St next;
          }Ele_St, *PEle_St;
          //請求消息,信元流格式。
          typedef struct Req_Ele_Stream
          {
              Plugin_ID src_id;//源插件id
              Service_Type req_type;//請求類型
              PEle_St ele;
          } Req_Ele_Stream, *PReq_Ele_Stream;
          //響應消息,信元流格式。
          typedef struct Rsp_Ele_Stream
          {
              Plugin_ID dest_id;//目的插件id
              Service_Type req_type;//響應對應的請求的類型。
              Execute_Result result;//記錄執行結果
              Execute_Reason reason;//記錄執行結果的原因
              PEle_St ele;
          } Rsp_Ele_Stream, *PRsp_Ele_Stream;
          //接收插件調用服務請求函數,由插件提供,入參為請求信元流。返回值為響應信元流,用于同步請求處理。
          PRsp_Ele_Stream PF_Receive_Invoke_Proc(PReq_Ele_Stream pele_str);
          //插件收到響應消息的處理入口函數,由插件提供。如此為響應信元流。
          void PF_Receive_Rsponse_Porc(PRsp_Ele_Stream pele_str);

          插件間的依賴關系是通過信元流來實現的。至于信元流的使用在我的另一篇博客《使用信元流(TLVStream)規范、簡化模塊(C/C++)間交互 》 中有描述。插件對外的接口都是統一的。

          如果插件要和其他的插件通信,則調用PF_Init_Plugin函數的傳遞的服務調用接口: invoke_serv_func。插件框架根據信元流的類型,查找驅動表,找到對應的服務接收函數。插件用函數 PF_Receive_Invoke_Proc接受其他插件的請求,此函數是插件想插件框架主動注冊到驅動表的。

          如果服務時同步的,這直接通過此函數返回,返回的信息在響應信元流中。如果是異步的請求,這插件在處理完成后,通過 send_rsp_func函數來發送響應。

          插件的卸載
          //卸載插件時調用的函數,類似于析構函數。由插件提供,供插件框架卸載插件時調用。
          void PF_Destroy_Func();

          posted @ 2009-10-12 20:20 常高偉 閱讀(1231) | 評論 (0)編輯 收藏

          這幾天為了設計插件開發框架,嘗試用了一下發散思維來思考問題。中間看過依賴注入,AOP(面向方面編程),以及契約式設計等。雖然有些工具無法直接使用,但是這些思想還是可以借鑒的,比如依賴注入,契約式設計。至于AOP,和工具相關性較大,雖然思想不錯,但是無法直接在C++中使用。

          我設計的插件間的依賴不是通過接口實現的,而是通過插件間的數據(信元流)。而信元流的檢測可以使用契約來檢查。

          插件開發框架的總體結構

          結構圖

          微內核

          1、 負責插件的加載,檢測,初始化。

          2、 負責服務的注冊。

          3、 負責服務的調用。

          4、 服務的管理。

          擴展層:

          1、 日志的打印。

          2、 消息(信元流)的解釋,將二進制格式解釋為文本。便于定位。

          3、 消息和日志的追蹤。

          分布式處理層:

          1、 用于和其他的框架通信。

          2、 和其他的框架搭配,形成一個分布式的系統。

          自動化測試框架層:

          1、 集成 cppunit 。

          2、 自動化集成測試框架。

          3、 自動化功能測試框架。

          和第三方框架集成層:

          1 、和 第三方框架 集成層。

          posted @ 2009-10-12 20:19 常高偉 閱讀(756) | 評論 (0)編輯 收藏

          原創  使用信元流(TLVStream)規范、簡化模塊(C/C++)間交互 收藏

            問題描述:
            在軟件開發過程中,一般會對復雜的現實世界進行抽象,并且采用分而治之的策略,將大系統分解為子系統,將子系統分解為一個個的模塊。模塊間的通信一般采用函數調用的方式,這樣會產生一些問題:
             1. 模塊間的接口增多會導致模塊間緊密耦合,不利于模塊的重用、調試、維護。
             2. 接口函數參數會很復雜,容易出現很龐大,功能面面俱到的結構體。
             3. 不利于擴展,新增功能要新增接口函數,或者修改接口參數。


          信元流的定義
            信元流是一種處理數據的方式。它對現實世界的數據進行抽象,形成一個一個的信元,然后由這些信元以一定的方式組合起來,形成信元流,來傳遞數據。下面信元流的實現。
            信元的實現
            信元由三部分組成,分別是信元標識(Tag),信元長度(Length),信元值(Value),即TLV格式。下面是一個信元的c定義:   
            typedef struct __ELEMENT_ST

               {

                    ELE_TAG tag;

                    ELE_LEN len;

                    ELE_VALUE value;

               }ELEMENT_ST;

            信元流的實現:
            信元流分為兩部分,信元流頭部(head)和體部(body)。head部分用于記錄信元流的整體描述,比如,信元流的起始模塊,目的模塊,以及信元流的消息類型等等。當然,也可以根據需要自己進行擴展。body部分包括信元流中信元的總大小,即ELE_MSG_BODY中body的長度。

                  typedef struct __ELEMENT_STREAM_ST
                  {
                      MOD_ID src_mod;
                      MOD_ID des_mod;
                      ELE_MSG_TYPE type;
                      ELE_MSG_BODY body;
                  }ELEMENT_STREAM_ST;
                  typedef struct __ELE_MSG_BODY
                  {
                      ELE_MSG_LEN len;
                      char body[MAX_ELE_MSG_BODY_LEN];
                  } ELE_MSG_BODY;

            構造信元流
            定義好結構體后,下面定義兩個函數,分別向信元體中添加信元和刪除信元。
             //pbody信元體的指針。這里沒有使用信元流的指針是為了讓函數的重用性更好,用戶可以自己定義信元流。
                  //tag:添加信元的TAG值,len:添加信元的長度。pvale:添加信元的值。
                  int AddElementToStreamBody(ELE_MSG_BODY *pbody, ELE_TAG tag, ELE_LEN len, void *pvalue);
                  //pbody信元體的指針。 //tag:獲取信元的TAG值,buf_len:pbuf的長度。pbuf:目標緩沖區,要把信元VALUE的值寫入吃緩沖區。
                  int GetElementFromStreamBody(ELE_MSG_BODY *pbody, ELE_TAG tag, int buf_len, void *pbuf);
            信元流的body是一個緩沖區,信元在里面順序排列。信元在body中順序不影響它的獲取。添加和獲取的方法比較簡單,不再贅述。


            一個信元流的實例
            下面來舉一個具體的例子來介紹一下信元流的使用,以及它的優點。
            假如由兩個模塊A和B。A模塊負責處理業務邏輯,B模塊負責處理呼叫控制。A調用B的一個接口發起呼叫,接口如下。
                  typedef struct __MAKE_CALL_ST
                  {
                      char caller_num[MAX_NUM_LEN];//主叫號碼
                      char called_num[MAX_NUM_LEN];//被叫號碼
                  }MAKE_CALL_ST;
                  int MakeCall(MAKE_CALL_ST *pcall_info);

            后面需求有更改,某些情況下藥攜帶主叫的callid信息。結構體會變成:
              typedef struct __MAKE_CALL_ST
              {
                  char caller_num[MAX_NUM_LEN];//主叫號碼
                  char called_num[MAX_NUM_LEN];//被叫號碼
                  CALL_ID caller_callid;
              }MAKE_CALL_ST;
            某些情況下又需要攜帶主叫的SDP信息,結構體會變成:
              typedef struct __MAKE_CALL_ST
              {
                  char caller_num[MAX_NUM_LEN];//主叫號碼
                  char called_num[MAX_NUM_LEN];//被叫號碼
                  CALL_ID caller_callid;
                  SDP_INFO call_sdp;
              }MAKE_CALL_ST;
            隨著需求的增加,這個結構體會越來越大,并且,其中的成員在某些情況下要使用,某些情況下又不使用,造成模塊間的冗余數據。
            當然,你也可以采用其他的方法,比如,再多定義幾個接口和結構體。但是這樣的話接口和結構體的數量會呈爆炸式的增長。
            使用信元流可以很好的解決這個問題。把號碼,callid,sdp等全部定義成信元,需要的時候塞進去,不需要的話就不添加。另外還有一個好處就是,一個模塊可以對外只公布一個接口,來收取信元流,然后在根據信元流的類型進行分別處理。這樣,一個模塊對外就只有一個接口了,模塊間的耦合性會降低。


          一點改進
            上面定義的信元流的格式在實際使用的過程中還是碰到了一些問題,最突出的就是,信元流的大小是固定死的。這種情況下,如果信元信息很小,會導致空間浪費,效率降低;如果信元信息很多,信元流的空間又不夠。

            可以對上面的這種方案進行一下優化,把信元的定義更改為:

            typedef struct __ELEMENT_ST

               {

                    ELE_TAG tag;

                    ELE_LEN len;

                    ELE_VALUE value;

                    ELEMENT_ST  *pnext_ele;//下一個信元流

               }ELEMENT_ST;

            將信元流的定義更改為:

                  typedef struct __ELEMENT_STREAM_ST
                  {
                      MOD_ID src_mod;
                      MOD_ID des_mod;
                      ELE_MSG_TYPE type;
                      ELEMENT_ST  *pfirst_ele;//第一個信元流
                  }ELEMENT_STREAM_ST;

            將信元流和信元更改為動態申請的內存。這樣既可以提高效率,有沒有了大小的限制。

            需要增加兩個接口,來申請和釋放信元流。

            唯一不好的地方時,動態申請的內存需要程序員記得釋放,否則會內存泄露。不過還有一個方法,即增加一個申請信元流的函數,如下
                  ELEMENT_STREAM_ST *GetEleStream()
                  {
                      static     ELEMENT_STREAM_ST *pstream = NULL;
                      if (NULL != pstream)
                      {
                          FreeEleStream(pstream);   
                          pstream = NULL;
                      }
                      pstream = AllocteEleStream();
                      return pstream;
                  }

            這樣的話,通過函數GetEleStream獲取的信元流,只在函數范圍內有效,退出函數后,立即無效。

          posted @ 2009-10-12 20:18 常高偉 閱讀(362) | 評論 (0)編輯 收藏

          在這一系列的上一個文章中,介紹了構建C/C++插件開發框架的初步設想,下面我會一步步的向下展開,來實現我的這個設想。

          今天主要談一下我對這個框架的功能認識,或是期望。昨天看了一篇關于持續集成能力成熟度模型 的一篇文章,受此啟發,我對此框架的認識漸漸清晰。

          這個框架可以當做我們公司底層產品(交換機,資源服務器等)的基礎設施。上層基于java開發的產品可以直接在OSGI上開發。

          核心功能:

          1、最重要的一個功能是,提供一個模塊化的編程模型,促進模塊化軟件開發,真正的實現針對接口編程。

          2、提供一個有助于提高模塊可重用性的基礎設施。

          3、提供一個C/C++插件的運行環境。

          4、提供一個動態插件框架,插件可以動態更改,而無需重啟系統。這個功能雖然不難實現,但是用處好像不是很大。


          擴展部分功能:

          1、支持分布式系統結構,多個運行框架組合起來形成一個系統,對模塊內部隱藏遠程通訊細節。

          2、支持系統的分層架構。

          3、能夠和其他的開發框架進行集成,比如OSGI,SCA等。

          4、多個運行框架中,能夠實現對運行框架的有效管理。

          5、概念上要實現類似于SCA中component(構件),composite(組合構件),Domain(域)的概念。


          開發部分功能:

          1、為了簡化開發,開發一個Eclipse插件,用于開發框架中的C/C++插件。能夠根據插件開發向導,最終生成符合插件規范的公共代碼,配置文件,Makefile文件等。


          調試部分功能:

          1、提供一個統一的日志處理函數,可以集成Log4cpp。

          2、提供模塊間的消息日志,以及框架對外的接口日志。

          3、提供消息和日志的追蹤功能,能將和某事件相關的消息和日志單獨提取出來。

          4、提供資源監測功能,監測對資源(內存,套接字,文件句柄等)的使用情況。


          測試部分功能:

          1、集成一些單元測試框架,比如unitcpp,達到自動化單元測試的目標。

          2、自己實現自動化集成測試框架,并且開發相應的Eclipse插件,簡化集成測試(利用腳本和信元流)。

          3、集成原有的自動化功能測試框架flowtest,并且開發相應的Eclipse插件,簡化功能測試。

          4、實現性能測試,監測框架。


          部署部分功能:

          1、實現自動化部署。特別是在分布式應用的情況下。

          2、提供一個命令行程序,通過命令更改系統配置,管理插件。

          posted @ 2009-10-12 20:18 常高偉 閱讀(860) | 評論 (0)編輯 收藏

           最近一直在學習OSGI方面的知識。買了一本《OSGI原理和最佳實踐》,可是還沒有到。遺憾的是,OSGI目前的幾個開源框架只支持Java,對C和C++都不支持的。可惜我們公司目前主要的開發語言還是c和c++,即便是引進OSGI,所得的好處范圍有限。而我對松散耦合的模塊化開發向往已久。查了一下OSGI對C++支持的好像是有一個開源項目,不過好像應用范圍很小。而SCA標準中是有對C++實現模型的支持的,但是幾個開源的框架目前還只支持JAVA。

            昨天看了丁亮的轉載的一篇博客《C/C++:構建你自己的插件框架 》,原文的鏈接:http://blog.chinaunix.net/u/12783/showart_662937.html 。看了一下里面講的方法,自己倒是可以實現。所以有了構建自己的c/c++插件開發框架的想法。今天先寫一下初步的設想。

          C/C++插件開發框架的要素

            BlueDavy有一篇介紹服務框架要素的文章(鏈接:http://www.aygfsteel.com/BlueDavy/archive/2009/08/28/172259.html )。我的插件框架也要考慮、解決以下的幾個問題:

            1、如何注冊插件;

            2、如何調用插件;

            3、如何測試插件;

            4、插件的生命周期管理;

            5、插件的管理和維護;

            6、插件的組裝;

            7、插件的出錯處理;

            8、服務事件的廣播和訂閱(這個目前還沒有考慮要支持);

            其中有幾個點很重要:1)插件框架要能夠使模塊松散耦合,做到真正的面向接口編程;2)框架要支持自動化測試:包括單元測試,集成測試;3)簡化部署;4)支持分布式,模塊可以調用框架外的插件。

          采用的技術
            插件框架要解決的一個問題就是插件的動態加載能力。這里可以使用共享庫的動態加載技術。當然,為了簡單,第一步只考慮做一個linux下的插件框架。

            總體結構

            框架的總體結構上,參考OSGI的“微內核+系統插件+應用插件”結構。這里要好好考慮一下把什么做在內核中。關于微內核結構,以前我做個一個微內核流程引擎,會在后面有時間和大家分享。

            框架中模塊間的數據傳送,有兩種解決方法:一是普元采用的XML數據總線的做法。優點是擴展性好,可讀性好。但是速度有些慢。二是采用我熟悉的信元流。優點的效率高,訪問方便,但是可讀性差一點,另外跨框架的數據傳送,需要考慮網絡字節序的問題。

            對于框架間的通信,通過系統插件封裝,對應用插件隱藏通信細節。

                部署

                努力做到一鍵式部署。

          posted @ 2009-10-12 20:16 常高偉 閱讀(2428) | 評論 (0)編輯 收藏


          僅列出標題  下一頁

          posts - 19, comments - 0, trackbacks - 0, articles - 0

          Copyright © 常高偉

          主站蜘蛛池模板: 梧州市| 奉化市| 格尔木市| 班戈县| 永济市| 临沭县| 深州市| 望谟县| 岑巩县| 怀化市| 巫溪县| 汝城县| 龙门县| 汝州市| 彭泽县| 手机| 洛隆县| 上虞市| 上思县| 吉林省| 苍梧县| 龙里县| 阿拉善右旗| 娱乐| 讷河市| 南宁市| 安岳县| 瓦房店市| 仙游县| 南溪县| 镇康县| 镇江市| 湘潭县| 修文县| 松潘县| 淮阳县| 商水县| 巴彦淖尔市| 天津市| 台南市| 农安县|