2009年10月12日

          1.1 BUILDER生成器

          1、 意圖

          將一個(gè)復(fù)雜對(duì)象的構(gòu)建和它的表示分離,使得同樣的構(gòu)建過(guò)程可以創(chuàng)建不同的表示。

          構(gòu)建是通過(guò)對(duì)生成器抽象接口的調(diào)用實(shí)現(xiàn)的。而構(gòu)建出來(lái)的產(chǎn)品的類型(表示),是由具體的進(jìn)行構(gòu)建的生成器的子類的實(shí)例來(lái)決定的。這樣導(dǎo)向器只需要調(diào)用生成器的抽象接口生成產(chǎn)品,而具體生成什么樣的產(chǎn)品,則是根據(jù)導(dǎo)向器配置的具體的生成器實(shí)例相關(guān)。

          終于理解了它是如何把復(fù)雜對(duì)象的構(gòu)建和它的表示分離的,也理解了為什么同樣的構(gòu)建過(guò)程。

          2、 動(dòng)機(jī)

          3、 適用性

          當(dāng)創(chuàng)建復(fù)雜對(duì)象的算法應(yīng)該獨(dú)立于該對(duì)象的組成以及他們的裝配方式時(shí)——對(duì)象的組成及裝配方式是生成器的抽象接口來(lái)表示的。而對(duì)象的創(chuàng)建的算法是有生成器的子類實(shí)現(xiàn)的。

          當(dāng)構(gòu)造過(guò)程必須允許被構(gòu)造的對(duì)象有不同的表示時(shí)——也就是,同樣的構(gòu)造過(guò)程,可以生成不同的產(chǎn)品。這里是通過(guò)配置導(dǎo)向器的生成器的子類實(shí)例來(lái)實(shí)現(xiàn)的。

          4、 結(jié)構(gòu)

          wps_clip_image-372

          5、 參與者

          l Builder:為創(chuàng)建一個(gè)product對(duì)象的各個(gè)部件指定抽象接口。這些部件最終構(gòu)成了product,而對(duì)這些抽象接口的調(diào)用,則是裝配product的操作,調(diào)用的次數(shù),入?yún)⒌炔煌瑒t最終生成的product也不同。product本身的構(gòu)造過(guò)程可能會(huì)非常復(fù)雜。但是,這些復(fù)雜度對(duì)Director(導(dǎo)向者)是隱藏的。這些抽象的接口描述了產(chǎn)品的組成以及裝配方式。

          l ConcreteBuilder:實(shí)現(xiàn)Builder的接口以構(gòu)造和裝配產(chǎn)品的各個(gè)部件;定義并明確它所創(chuàng)建的表示;提供一個(gè)檢索產(chǎn)品的接口。這個(gè)類實(shí)現(xiàn)了Builder的抽象接口,從而可以創(chuàng)建不同的表示,但是組成和裝配過(guò)程還是一樣的。

          l Director:構(gòu)造一個(gè)使用Builder抽象接口的對(duì)象。更對(duì)象根據(jù)Builder的接口來(lái)裝配并生產(chǎn)產(chǎn)品。

          l Product:表示被構(gòu)造的復(fù)雜的對(duì)象。ConcreteBuilder創(chuàng)建該產(chǎn)品的內(nèi)部表示,并定義它的裝配過(guò)程;包含定義組成部件的類,包括將這些部件裝配成最終產(chǎn)品的接口。

          6、 協(xié)作

          l 客戶創(chuàng)建Director對(duì)象,并用它想要的Builder對(duì)象進(jìn)行配置。

          l 一旦產(chǎn)品不僅被生成,導(dǎo)向器就會(huì)通知生成器。

          l 生成器處理導(dǎo)向器請(qǐng)求,并且將部件添加到該產(chǎn)品中。

          l 客戶從生成器中檢索產(chǎn)品。

          wps_clip_image-911

          7、 效果

          l 它使你可以改變一個(gè)產(chǎn)品的內(nèi)部表示。Builder提供給Director的抽象接口可以使生成器隱藏這個(gè)產(chǎn)品的表示和內(nèi)部結(jié)構(gòu)。它同時(shí)也隱藏了該產(chǎn)品時(shí)如何裝配的。因?yàn)楫a(chǎn)品時(shí)通過(guò)抽象接口構(gòu)造的,你改變?cè)摦a(chǎn)品的內(nèi)部表示時(shí),所要做的只是定義一個(gè)新的生成器。

          l 它將構(gòu)造代碼和表示代碼分開(kāi)。Builder模式通過(guò)封裝一個(gè)復(fù)雜對(duì)象的創(chuàng)建和表示方式提高了對(duì)象的模塊性。每個(gè)ConcreteBuilder包含了創(chuàng)建和裝配一個(gè)特定產(chǎn)品的所有代碼。

          l 它可以使你對(duì)構(gòu)造過(guò)程進(jìn)行更精細(xì)的控制。

          8、 實(shí)現(xiàn)

          l 通常一個(gè)抽象的Builder類為導(dǎo)向者可能要求創(chuàng)建的每一個(gè)構(gòu)件定義一個(gè)操作。這些操作缺省什么也不做。一個(gè)ConcreteBuilder類對(duì)它有興趣創(chuàng)建的構(gòu)建重定義這些操作。

          l 裝配和構(gòu)造接口:一般構(gòu)造請(qǐng)求的結(jié)果只是被添加到產(chǎn)品中,特殊情況下,需要返回給導(dǎo)向器。

          l 產(chǎn)品沒(méi)有抽象類:一般他們沒(méi)有公共的部分。如果有也可以設(shè)置一個(gè)抽象類。

          l Builder中缺省的方法為空。

          9、 代碼示例

          class MazeBuilder {

          public:

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

              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,提供部件的具體構(gòu)造代碼

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

              builder.BuildMaze();

              builder.BuildRoom(1);

              builder.BuildRoom(2);

              builder.BuildDoor(1, 2);

              return builder.GetMaze();

          }//CreateMaze是導(dǎo)向器,調(diào)用生成器的抽象接口完成產(chǎn)品的構(gòu)造過(guò)程。

          //下面代碼描述產(chǎn)品的構(gòu)造過(guò)程

          Maze* maze;//最終的產(chǎn)品

          MazeGame game;//Director,導(dǎo)航者

          StandardMazeBuilder builder;//ConcreteBuilder,實(shí)際的構(gòu)造類

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

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

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

          1.1 ABSTRACT FACTORY 抽象工廠

          1、 意圖

          提供一個(gè)創(chuàng)建一系列相關(guān)或相互依賴對(duì)象的接口,而無(wú)需指定他們具體的類。

          2、 動(dòng)機(jī)

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

          這里的主要的思想是封裝對(duì)象的創(chuàng)建的過(guò)程。客戶端可以不需要知道具體要?jiǎng)?chuàng)建那些對(duì)象,而只需要知道創(chuàng)建某一系列的對(duì)象所用到的“工廠對(duì)象”即可。

          3、 適用性

          一個(gè)系統(tǒng)要獨(dú)立于它的產(chǎn)品的創(chuàng)建、組合和表示時(shí)。 

          一個(gè)系統(tǒng)要由多個(gè)產(chǎn)品系列中的一個(gè)來(lái)配置時(shí)。 

          當(dāng)你要強(qiáng)調(diào)一系列相關(guān)的產(chǎn)品對(duì)象的設(shè)計(jì)以便進(jìn)行聯(lián)合使用時(shí)。 

          當(dāng)你提供一個(gè)產(chǎn)品類庫(kù),而只想顯示它們的接口而不是實(shí)現(xiàn)時(shí)。 

          4、 結(jié)構(gòu)

          wps_clip_image-278

          5、 參與者

          AbstractFactory:創(chuàng)建一系列對(duì)象的抽象類。

          ConcreteFactory:實(shí)現(xiàn)具體創(chuàng)建產(chǎn)品對(duì)象的操作。

          AbstractProduct:為一類產(chǎn)品對(duì)象聲明一個(gè)接口。

          ConcreteProduct:定義一個(gè)被相應(yīng)的具體工廠創(chuàng)建的對(duì)象;實(shí)現(xiàn)AbstractProduct接口。

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

          6、 協(xié)作

          在運(yùn)行時(shí)刻,創(chuàng)建一個(gè)ConcreteFactory實(shí)例,它創(chuàng)建具有特定實(shí)現(xiàn)的對(duì)象。為創(chuàng)建不同的對(duì)象,客戶應(yīng)使用不同的具體工廠。

          AbstractFactory將具體對(duì)象的創(chuàng)建延遲到它的子類ConcreteFactory中。

          7、 效果

          1) 它分離了具體的類:一個(gè)工廠封裝創(chuàng)建產(chǎn)品的責(zé)任和過(guò)程,它將客戶和類的實(shí)現(xiàn)分離。客戶通過(guò)抽象接口操作實(shí)例。產(chǎn)品的類名也在具體工廠實(shí)現(xiàn)中分離,他們不出現(xiàn)在客戶代碼中。

          2) 它使得易于交互產(chǎn)品系列。

          3) 它有利于產(chǎn)品的一致性。

          4) 難于支持新的種類。

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

          1.1 設(shè)計(jì)模式怎樣解決設(shè)計(jì)問(wèn)題

          1.1.1 尋找合適的對(duì)象

          面向?qū)ο笤O(shè)計(jì)最困難的部分是將系統(tǒng)分解為對(duì)象的集合。

          設(shè)計(jì)的許多對(duì)象來(lái)源于現(xiàn)實(shí)世界的分析模型,這里和領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)有點(diǎn)關(guān)聯(lián)。分析所得到的類,很多事現(xiàn)實(shí)中并不存在的類。這是抽象的結(jié)果。設(shè)計(jì)中的抽象對(duì)于產(chǎn)生靈活的設(shè)計(jì)至關(guān)重要。就像我設(shè)計(jì)的一個(gè)流程調(diào)度模型。

          1.1.2 決定對(duì)象的粒度

          記筆記可以讓我達(dá)到沉流的狀態(tài)。

          1.1.3 指定對(duì)象接口
          1.1.4 描述對(duì)象實(shí)現(xiàn)

          OMT表示法:

          1、 對(duì)象:最上面的黑體表示類名,下面依次是操作,數(shù)據(jù)。

          wps_clip_image-219

          2、 實(shí)例化:虛線箭頭表示一個(gè)類實(shí)例化另外一個(gè)對(duì)象。

          wps_clip_image-245

          3、 繼承:豎線和三角表示繼承關(guān)系。

          wps_clip_image-263

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

          5、 引用

          wps_clip_image-293

          箭頭加黑點(diǎn)表示一個(gè)類引用另外一個(gè)類。

          重點(diǎn):

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

          對(duì)象的類和對(duì)象的類型的區(qū)別:

          對(duì)象的類定義了對(duì)象是怎樣實(shí)現(xiàn)的,同時(shí)也定義了對(duì)象內(nèi)部狀態(tài)和操作的實(shí)現(xiàn)。對(duì)象的類型只與它的接口有關(guān)。一個(gè)對(duì)象可以由多個(gè)類型(支持多個(gè)接口),不同類的對(duì)象可以有相同的類型。

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

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

          c++中接口繼承接近于公有繼承純抽象類。純實(shí)現(xiàn)繼承或純類繼承接近于私有繼承。

          2、 對(duì)接口編程,而不是對(duì)實(shí)現(xiàn)編程——面向?qū)ο笤O(shè)計(jì)的第一個(gè)原則

          1.1.5 運(yùn)用復(fù)用機(jī)制

          1、 繼承和組合的比較

          繼承是一種白箱復(fù)用,父類的內(nèi)部細(xì)節(jié)對(duì)子類可見(jiàn)。

          對(duì)象組合彼此不知道對(duì)方內(nèi)部細(xì)節(jié),成為黑箱復(fù)用。

          繼承的優(yōu)缺點(diǎn):

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

          2) 編譯時(shí)刻決定了,無(wú)法在運(yùn)行期間更改。

          3) 子類要知道父類的實(shí)現(xiàn)細(xì)節(jié),這樣就部分破壞了封裝性。子類和父類依賴過(guò)于緊密,父類的某些變化必然導(dǎo)致子類的變化。開(kāi)發(fā)過(guò)程中遇到過(guò)類似的問(wèn)題。這種依賴,限制了靈活性以及復(fù)用性。比如,服務(wù)體系中經(jīng)常出現(xiàn)這樣的問(wèn)題,導(dǎo)致代碼拷貝。

          組合(通過(guò)獲得對(duì)象的引用而在運(yùn)行時(shí)刻動(dòng)態(tài)的定義)的優(yōu)缺點(diǎn):

          1) 對(duì)象間通過(guò)接口彼此交互。

          2) 對(duì)象只能通過(guò)接口訪問(wèn),不要也不能知道對(duì)方細(xì)節(jié),這樣不會(huì)破壞封裝性。

          3) 運(yùn)行時(shí)刻可以使用另外一個(gè)對(duì)象替換這個(gè)對(duì)象,提高了靈活性。

          4) 對(duì)象的實(shí)現(xiàn)基于接口編寫(xiě),所以實(shí)現(xiàn)上存在較少的依賴關(guān)系。

          5) 優(yōu)先使用組合有助于保持每個(gè)類被封裝,并被集中在單個(gè)任務(wù)上,提高整體內(nèi)聚性。類和類的層次都維持一個(gè)較小的規(guī)模,

          6) 基于對(duì)象組合的設(shè)計(jì)會(huì)有更多的對(duì)象(而又較少的類),且系統(tǒng)的行為依賴于對(duì)象間的關(guān)系而不是定義在某個(gè)類的內(nèi)部。

          理想的情況下,應(yīng)該通過(guò)組合原有構(gòu)件實(shí)現(xiàn)新的功能,而不是創(chuàng)建新的構(gòu)件。

          面向?qū)ο笤O(shè)計(jì)的第二個(gè)原則:優(yōu)先使用對(duì)象組合,而不是類繼承。

          2、 委托

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

          委托的主要優(yōu)點(diǎn)在于它便于在運(yùn)行時(shí)刻組合對(duì)象操作,以及更改操作的組合方式。它是軟件更加的靈活。

          和其他的技術(shù)方案相同,它也存在不足之處:增加了軟件的復(fù)雜度——?jiǎng)討B(tài)的,高度參數(shù)化的軟件比靜態(tài)的軟件更難于理解。

          3、 繼承和參數(shù)化類型的比較

          1.1.6 關(guān)聯(lián)運(yùn)行時(shí)刻的結(jié)構(gòu)和編譯時(shí)刻的結(jié)構(gòu)
          1.1.7 設(shè)計(jì)應(yīng)支持變化

          設(shè)計(jì)應(yīng)該支持變化——所說(shuō)的是,一個(gè)設(shè)計(jì)方案,對(duì)變化要有一定的適應(yīng)性,即封裝變化。

          變化是導(dǎo)致重新設(shè)計(jì)的原因。設(shè)計(jì)要對(duì)一定范圍內(nèi)的變化友好。

          4、 

          對(duì)于程序的分層設(shè)計(jì),對(duì)于處于同一分層的模塊,對(duì)外應(yīng)保持一定的抽象,并且,使用同種類型的通信協(xié)議。

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

          1.1 變量存儲(chǔ)域

          1.1.1 一個(gè)示例

          pang123hui首先提供了一個(gè)網(wǎng)上流傳的學(xué)習(xí)代碼示例:

          int a = 0; //全局區(qū) 

          void main() 

          {

          int b; //棧 

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

          char *p1,*p2; //棧 

          char *p3 = "123456"; //123456在常量區(qū),p3在棧上 

          static int c =0; //全局區(qū) 

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

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

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

          }

          這個(gè)代碼示例中出現(xiàn)了“全局區(qū)”,“棧”,“文字常量區(qū)”,“堆”等詞語(yǔ)。為了統(tǒng)一,我們使用《C專家編程》中的說(shuō)法:堆棧段,BSS段,數(shù)據(jù)段,文本段。

          各個(gè)段的作用如下:

          1、 文本段:包含程序的指令,它在程序的執(zhí)行過(guò)程中一般不會(huì)改變。

          2、 數(shù)據(jù)段:包含了經(jīng)過(guò)初始化的全局變量和靜態(tài)變量,以及他們的值。

          3、 BSS段:包含未經(jīng)初始化的全局變量和靜態(tài)變量。

          4、 堆棧段:包含了函數(shù)內(nèi)部聲明的局部變量。

          當(dāng)然,上面段的作用不僅于此,具體的作用會(huì)在下面的知識(shí)點(diǎn)中介紹。

          1.1.2 通過(guò)代碼測(cè)試變量的存儲(chǔ)位置

          Linux下可以通過(guò)系統(tǒng)命令“size”查看可以執(zhí)行程序各個(gè)段的大小。但是,可執(zhí)行程序中的段結(jié)構(gòu)和運(yùn)行中程序在內(nèi)存中的段結(jié)構(gòu)并不完全相同,但是有一定的映射關(guān)系。具體如下圖所示(圖片信息來(lái)自《C專家編程》):

          wps_clip_image-696

          下面通過(guò)代碼示例和“size”來(lái)研究變量的存儲(chǔ)區(qū)域。

          test.c

          int main()

          {

          return 1;

          }

          編譯,并且查看可執(zhí)行程序各個(gè)段的大小:

          wps_clip_image-779

          更改test.c:

          int g_data;

          int main()

          {

          return 1;

          }

          編譯,并且查看可執(zhí)行程序各個(gè)段的大小:

          wps_clip_image-849

          可以發(fā)現(xiàn),文本段,數(shù)據(jù)段都沒(méi)有發(fā)送變化,而B(niǎo)SS段增加了4個(gè)字節(jié)。

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

          繼續(xù):

          int g_data = 1;

          int main()

          {

          return 1;

          }

          編譯:

          wps_clip_image-958

          可以發(fā)現(xiàn),BSS段和文本段相同,而數(shù)據(jù)段增加了4個(gè)字節(jié)。

          結(jié)論2:經(jīng)過(guò)初始化的全局變量保存在數(shù)據(jù)段中

          繼續(xù):

          int main()

          {

          static int g_data;

          return 1;

          }

          編譯:

          wps_clip_image-1066

          可以發(fā)現(xiàn),文本段,數(shù)據(jù)段都沒(méi)有發(fā)送變化,而B(niǎo)SS段增加了4個(gè)字節(jié)。

          結(jié)論3:未初始化的靜態(tài)變量保存在BSS段中

          繼續(xù):

          int main()

          {

          static int g_data = 1;

          return 1;

          }

          編譯:

          wps_clip_image-1183

          可以發(fā)現(xiàn),BSS段和文本段相同,而數(shù)據(jù)段增加了4個(gè)字節(jié)。

          結(jié)論4:經(jīng)過(guò)初始化的靜態(tài)變量保存在數(shù)據(jù)段中

          繼續(xù):

          int main()

          {

          int i_data = 1;

          return 1;

          }

          編譯:

          wps_clip_image-1288

          可以發(fā)現(xiàn),BSS段和和數(shù)據(jù)段相同,而文本段增加了16個(gè)字節(jié)。局部變量會(huì)在執(zhí)行的時(shí)候在堆棧段中生成,函數(shù)執(zhí)行完畢后釋放。

          結(jié)論5:函數(shù)內(nèi)部聲明的局部變量保存在堆棧段中

          繼續(xù):

          const int g_data = 1;

          int main()

          {

          return 1;

          }

          編譯:

          wps_clip_image-1430

          把全局變量定義為“const”后,也許你會(huì)感到奇怪,怎么BSS段和數(shù)據(jù)段都沒(méi)有發(fā)生變化,而文本段卻增加了4個(gè)字節(jié)。

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

          那么,const的局部變量?

          繼續(xù):

          int main()

          {

          const int i_data = 1;

          return 1;

          }

          編譯:

          wps_clip_image-1587

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

          繼續(xù):

          char *pstr = "";

          int main()

          {

          return 1;

          }

          編譯:

          wps_clip_image-1666

          在做一下更改:

          char *pstr = "123456789";

          int main()

          {

          return 1;

          }

          編譯:

          wps_clip_image-1733

          可以發(fā)現(xiàn),前后數(shù)據(jù)段和BSS段大小均未發(fā)生變化,而文本段增加了9個(gè)字節(jié)。

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

          1.1.3 結(jié)論

          1、 經(jīng)過(guò)初始化的全局變量和靜態(tài)變量保存在數(shù)據(jù)段中。

          2、 未經(jīng)初始化的全局變量和靜態(tài)變量保存在BSS段。

          3、 函數(shù)內(nèi)部聲明的局部變量保存在堆棧段中。

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

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

          1.1.4 擴(kuò)展閱讀

          《C專家編程》第6章——詳細(xì)介紹各個(gè)段的作用。

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

          讀S計(jì)劃的理念

          自助、互助,共同進(jìn)步!

           

          讀S計(jì)劃的初衷

          現(xiàn)在有很多學(xué)習(xí)方式,搜索引擎、論壇、博客,qq群等等,那么我這樣的計(jì)劃還有存在的必要么?這個(gè)計(jì)劃的獨(dú)特之處在哪里?

          讀S計(jì)劃的獨(dú)特之處不在于其學(xué)習(xí)內(nèi)容和方式,學(xué)習(xí)的內(nèi)容我們可以根據(jù)實(shí)際情況調(diào)整,可以由多個(gè)人同時(shí)引導(dǎo)多個(gè)學(xué)習(xí)方向,這些都不是程式化的,也不是重點(diǎn)。

          讀S計(jì)劃的獨(dú)特之處在于其理念:致力于形成一種有計(jì)劃、有組織,強(qiáng)調(diào)互動(dòng)、互助和共同進(jìn)步的學(xué)習(xí)模式和氛圍。讀S計(jì)劃不是一個(gè)單純的興趣小組,我們會(huì)按照計(jì)劃不斷的切換話題。讀S計(jì)劃也不是提問(wèn)的好地方,在每個(gè)階段,我們都有限定的討論范圍,所以無(wú)關(guān)的問(wèn)題很容易被忽略。讀S計(jì)劃希望多數(shù)人以積極的心態(tài)參與進(jìn)來(lái),更多的討論、研究和貢獻(xiàn)您的想法,而不是被動(dòng)的接受和看,我們不希望將學(xué)習(xí)計(jì)劃做成課堂模式,我們希望做成項(xiàng)目的模式 。讀S計(jì)劃致力于將信息以信息本身進(jìn)行聚合,而不是以人進(jìn)行聚合,因?yàn)閷W(xué)習(xí)計(jì)劃在不斷推進(jìn),所以不存在永遠(yuǎn)的權(quán)威人士,但知識(shí)本身是客觀、中立的、權(quán)威的。

           

          共建新型技術(shù)社區(qū)

          建立新型的、關(guān)系緊密的、以知識(shí)體系為軸、強(qiáng)調(diào)經(jīng)驗(yàn)積累與分享的的技術(shù)社區(qū)。

           

          更深入了解此計(jì)劃,可以閱讀:

          《技術(shù)族譜 之 讀S計(jì)劃:讓我們一起快樂(lè)學(xué)習(xí)》

          《準(zhǔn)備啟動(dòng)一個(gè)開(kāi)源項(xiàng)目 - 技術(shù)族譜 - 先期利用Goolge云計(jì)算平臺(tái)》

          《關(guān)于“讀S計(jì)劃”的聊天記錄:統(tǒng)一理念,確定目前的工作》

          《授人以“魚(yú)”,不如授人以“漁”,放棄一個(gè)目標(biāo),設(shè)定另一個(gè)目標(biāo)》

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

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

          上面一篇文章大致描述了一下插件開(kāi)發(fā)框架整體結(jié)構(gòu)。這篇描述一下核心層的設(shè)計(jì)和實(shí)現(xiàn)。

          至于核心層的設(shè)計(jì),我想借鑒 一下微內(nèi)核的思想。核心層只負(fù)責(zé)實(shí)現(xiàn)下面幾個(gè)功能:

          1、 插件的加載,檢測(cè),初始化。

          2、 服務(wù)的注冊(cè)。

          3、 服務(wù)的調(diào)用。

          4、 服務(wù)的管理。

          插件的加載,檢測(cè),初始化

          插件的加載利用linux共享庫(kù)的動(dòng)態(tài)加載技術(shù)。具體的方法可以看一下IBM網(wǎng)站的一篇資料《Linux 動(dòng)態(tài)庫(kù)剖析》

          服務(wù)的注冊(cè)

          服務(wù)的注冊(cè)與調(diào)用采用表驅(qū)動(dòng)的方法。核心層中維護(hù)一個(gè)服務(wù)注冊(cè)表。

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

          驅(qū)動(dòng)表是一個(gè)數(shù)組,下標(biāo)為插件間交互消息類型,成員為插件提供的接收的消息處理函數(shù),由插件初始化的時(shí)候,調(diào)用插件框架的的注冊(cè)函數(shù)注冊(cè)到驅(qū)動(dòng)表。

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

                插件在函數(shù)PF_Init_Plugin中調(diào)用函數(shù)register_func來(lái)注冊(cè)插件要處理的消息類型。

          服務(wù)的調(diào)用
          //信元結(jié)構(gòu)體
          typedef struct Ele_St
          {
              Ele_Tag tag;
              Ele_Length len;
              Ele_Value  value;
              PEle_St next;
          }Ele_St, *PEle_St;
          //請(qǐng)求消息,信元流格式。
          typedef struct Req_Ele_Stream
          {
              Plugin_ID src_id;//源插件id
              Service_Type req_type;//請(qǐng)求類型
              PEle_St ele;
          } Req_Ele_Stream, *PReq_Ele_Stream;
          //響應(yīng)消息,信元流格式。
          typedef struct Rsp_Ele_Stream
          {
              Plugin_ID dest_id;//目的插件id
              Service_Type req_type;//響應(yīng)對(duì)應(yīng)的請(qǐng)求的類型。
              Execute_Result result;//記錄執(zhí)行結(jié)果
              Execute_Reason reason;//記錄執(zhí)行結(jié)果的原因
              PEle_St ele;
          } Rsp_Ele_Stream, *PRsp_Ele_Stream;
          //接收插件調(diào)用服務(wù)請(qǐng)求函數(shù),由插件提供,入?yún)檎?qǐng)求信元流。返回值為響應(yīng)信元流,用于同步請(qǐng)求處理。
          PRsp_Ele_Stream PF_Receive_Invoke_Proc(PReq_Ele_Stream pele_str);
          //插件收到響應(yīng)消息的處理入口函數(shù),由插件提供。如此為響應(yīng)信元流。
          void PF_Receive_Rsponse_Porc(PRsp_Ele_Stream pele_str);

          插件間的依賴關(guān)系是通過(guò)信元流來(lái)實(shí)現(xiàn)的。至于信元流的使用在我的另一篇博客《使用信元流(TLVStream)規(guī)范、簡(jiǎn)化模塊(C/C++)間交互 》 中有描述。插件對(duì)外的接口都是統(tǒng)一的。

          如果插件要和其他的插件通信,則調(diào)用PF_Init_Plugin函數(shù)的傳遞的服務(wù)調(diào)用接口: invoke_serv_func。插件框架根據(jù)信元流的類型,查找驅(qū)動(dòng)表,找到對(duì)應(yīng)的服務(wù)接收函數(shù)。插件用函數(shù) PF_Receive_Invoke_Proc接受其他插件的請(qǐng)求,此函數(shù)是插件想插件框架主動(dòng)注冊(cè)到驅(qū)動(dòng)表的。

          如果服務(wù)時(shí)同步的,這直接通過(guò)此函數(shù)返回,返回的信息在響應(yīng)信元流中。如果是異步的請(qǐng)求,這插件在處理完成后,通過(guò) send_rsp_func函數(shù)來(lái)發(fā)送響應(yīng)。

          插件的卸載
          //卸載插件時(shí)調(diào)用的函數(shù),類似于析構(gòu)函數(shù)。由插件提供,供插件框架卸載插件時(shí)調(diào)用。
          void PF_Destroy_Func();

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

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

          我設(shè)計(jì)的插件間的依賴不是通過(guò)接口實(shí)現(xiàn)的,而是通過(guò)插件間的數(shù)據(jù)(信元流)。而信元流的檢測(cè)可以使用契約來(lái)檢查。

          插件開(kāi)發(fā)框架的總體結(jié)構(gòu)

          結(jié)構(gòu)圖

          微內(nèi)核

          1、 負(fù)責(zé)插件的加載,檢測(cè),初始化。

          2、 負(fù)責(zé)服務(wù)的注冊(cè)。

          3、 負(fù)責(zé)服務(wù)的調(diào)用。

          4、 服務(wù)的管理。

          擴(kuò)展層:

          1、 日志的打印。

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

          3、 消息和日志的追蹤。

          分布式處理層:

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

          2、 和其他的框架搭配,形成一個(gè)分布式的系統(tǒng)。

          自動(dòng)化測(cè)試框架層:

          1、 集成 cppunit 。

          2、 自動(dòng)化集成測(cè)試框架。

          3、 自動(dòng)化功能測(cè)試框架。

          和第三方框架集成層:

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

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

          原創(chuàng)  使用信元流(TLVStream)規(guī)范、簡(jiǎn)化模塊(C/C++)間交互 收藏

            問(wèn)題描述:
            在軟件開(kāi)發(fā)過(guò)程中,一般會(huì)對(duì)復(fù)雜的現(xiàn)實(shí)世界進(jìn)行抽象,并且采用分而治之的策略,將大系統(tǒng)分解為子系統(tǒng),將子系統(tǒng)分解為一個(gè)個(gè)的模塊。模塊間的通信一般采用函數(shù)調(diào)用的方式,這樣會(huì)產(chǎn)生一些問(wèn)題:
             1. 模塊間的接口增多會(huì)導(dǎo)致模塊間緊密耦合,不利于模塊的重用、調(diào)試、維護(hù)。
             2. 接口函數(shù)參數(shù)會(huì)很復(fù)雜,容易出現(xiàn)很龐大,功能面面俱到的結(jié)構(gòu)體。
             3. 不利于擴(kuò)展,新增功能要新增接口函數(shù),或者修改接口參數(shù)。


          信元流的定義
            信元流是一種處理數(shù)據(jù)的方式。它對(duì)現(xiàn)實(shí)世界的數(shù)據(jù)進(jìn)行抽象,形成一個(gè)一個(gè)的信元,然后由這些信元以一定的方式組合起來(lái),形成信元流,來(lái)傳遞數(shù)據(jù)。下面信元流的實(shí)現(xiàn)。
            信元的實(shí)現(xiàn)
            信元由三部分組成,分別是信元標(biāo)識(shí)(Tag),信元長(zhǎng)度(Length),信元值(Value),即TLV格式。下面是一個(gè)信元的c定義:   
            typedef struct __ELEMENT_ST

               {

                    ELE_TAG tag;

                    ELE_LEN len;

                    ELE_VALUE value;

               }ELEMENT_ST;

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

                  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;

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


            一個(gè)信元流的實(shí)例
            下面來(lái)舉一個(gè)具體的例子來(lái)介紹一下信元流的使用,以及它的優(yōu)點(diǎn)。
            假如由兩個(gè)模塊A和B。A模塊負(fù)責(zé)處理業(yè)務(wù)邏輯,B模塊負(fù)責(zé)處理呼叫控制。A調(diào)用B的一個(gè)接口發(fā)起呼叫,接口如下。
                  typedef struct __MAKE_CALL_ST
                  {
                      char caller_num[MAX_NUM_LEN];//主叫號(hào)碼
                      char called_num[MAX_NUM_LEN];//被叫號(hào)碼
                  }MAKE_CALL_ST;
                  int MakeCall(MAKE_CALL_ST *pcall_info);

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


          一點(diǎn)改進(jìn)
            上面定義的信元流的格式在實(shí)際使用的過(guò)程中還是碰到了一些問(wèn)題,最突出的就是,信元流的大小是固定死的。這種情況下,如果信元信息很小,會(huì)導(dǎo)致空間浪費(fèi),效率降低;如果信元信息很多,信元流的空間又不夠。

            可以對(duì)上面的這種方案進(jìn)行一下優(yōu)化,把信元的定義更改為:

            typedef struct __ELEMENT_ST

               {

                    ELE_TAG tag;

                    ELE_LEN len;

                    ELE_VALUE value;

                    ELEMENT_ST  *pnext_ele;//下一個(gè)信元流

               }ELEMENT_ST;

            將信元流的定義更改為:

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

            將信元流和信元更改為動(dòng)態(tài)申請(qǐng)的內(nèi)存。這樣既可以提高效率,有沒(méi)有了大小的限制。

            需要增加兩個(gè)接口,來(lái)申請(qǐng)和釋放信元流。

            唯一不好的地方時(shí),動(dòng)態(tài)申請(qǐng)的內(nèi)存需要程序員記得釋放,否則會(huì)內(nèi)存泄露。不過(guò)還有一個(gè)方法,即增加一個(gè)申請(qǐng)信元流的函數(shù),如下
                  ELEMENT_STREAM_ST *GetEleStream()
                  {
                      static     ELEMENT_STREAM_ST *pstream = NULL;
                      if (NULL != pstream)
                      {
                          FreeEleStream(pstream);   
                          pstream = NULL;
                      }
                      pstream = AllocteEleStream();
                      return pstream;
                  }

            這樣的話,通過(guò)函數(shù)GetEleStream獲取的信元流,只在函數(shù)范圍內(nèi)有效,退出函數(shù)后,立即無(wú)效。

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

          在這一系列的上一個(gè)文章中,介紹了構(gòu)建C/C++插件開(kāi)發(fā)框架的初步設(shè)想,下面我會(huì)一步步的向下展開(kāi),來(lái)實(shí)現(xiàn)我的這個(gè)設(shè)想。

          今天主要談一下我對(duì)這個(gè)框架的功能認(rèn)識(shí),或是期望。昨天看了一篇關(guān)于持續(xù)集成能力成熟度模型 的一篇文章,受此啟發(fā),我對(duì)此框架的認(rèn)識(shí)漸漸清晰。

          這個(gè)框架可以當(dāng)做我們公司底層產(chǎn)品(交換機(jī),資源服務(wù)器等)的基礎(chǔ)設(shè)施。上層基于java開(kāi)發(fā)的產(chǎn)品可以直接在OSGI上開(kāi)發(fā)。

          核心功能:

          1、最重要的一個(gè)功能是,提供一個(gè)模塊化的編程模型,促進(jìn)模塊化軟件開(kāi)發(fā),真正的實(shí)現(xiàn)針對(duì)接口編程。

          2、提供一個(gè)有助于提高模塊可重用性的基礎(chǔ)設(shè)施。

          3、提供一個(gè)C/C++插件的運(yùn)行環(huán)境。

          4、提供一個(gè)動(dòng)態(tài)插件框架,插件可以動(dòng)態(tài)更改,而無(wú)需重啟系統(tǒng)。這個(gè)功能雖然不難實(shí)現(xiàn),但是用處好像不是很大。


          擴(kuò)展部分功能:

          1、支持分布式系統(tǒng)結(jié)構(gòu),多個(gè)運(yùn)行框架組合起來(lái)形成一個(gè)系統(tǒng),對(duì)模塊內(nèi)部隱藏遠(yuǎn)程通訊細(xì)節(jié)。

          2、支持系統(tǒng)的分層架構(gòu)。

          3、能夠和其他的開(kāi)發(fā)框架進(jìn)行集成,比如OSGI,SCA等。

          4、多個(gè)運(yùn)行框架中,能夠?qū)崿F(xiàn)對(duì)運(yùn)行框架的有效管理。

          5、概念上要實(shí)現(xiàn)類似于SCA中component(構(gòu)件),composite(組合構(gòu)件),Domain(域)的概念。


          開(kāi)發(fā)部分功能:

          1、為了簡(jiǎn)化開(kāi)發(fā),開(kāi)發(fā)一個(gè)Eclipse插件,用于開(kāi)發(fā)框架中的C/C++插件。能夠根據(jù)插件開(kāi)發(fā)向?qū)В罱K生成符合插件規(guī)范的公共代碼,配置文件,Makefile文件等。


          調(diào)試部分功能:

          1、提供一個(gè)統(tǒng)一的日志處理函數(shù),可以集成Log4cpp。

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

          3、提供消息和日志的追蹤功能,能將和某事件相關(guān)的消息和日志單獨(dú)提取出來(lái)。

          4、提供資源監(jiān)測(cè)功能,監(jiān)測(cè)對(duì)資源(內(nèi)存,套接字,文件句柄等)的使用情況。


          測(cè)試部分功能:

          1、集成一些單元測(cè)試框架,比如unitcpp,達(dá)到自動(dòng)化單元測(cè)試的目標(biāo)。

          2、自己實(shí)現(xiàn)自動(dòng)化集成測(cè)試框架,并且開(kāi)發(fā)相應(yīng)的Eclipse插件,簡(jiǎn)化集成測(cè)試(利用腳本和信元流)。

          3、集成原有的自動(dòng)化功能測(cè)試框架flowtest,并且開(kāi)發(fā)相應(yīng)的Eclipse插件,簡(jiǎn)化功能測(cè)試。

          4、實(shí)現(xiàn)性能測(cè)試,監(jiān)測(cè)框架。


          部署部分功能:

          1、實(shí)現(xiàn)自動(dòng)化部署。特別是在分布式應(yīng)用的情況下。

          2、提供一個(gè)命令行程序,通過(guò)命令更改系統(tǒng)配置,管理插件。

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

           最近一直在學(xué)習(xí)OSGI方面的知識(shí)。買(mǎi)了一本《OSGI原理和最佳實(shí)踐》,可是還沒(méi)有到。遺憾的是,OSGI目前的幾個(gè)開(kāi)源框架只支持Java,對(duì)C和C++都不支持的。可惜我們公司目前主要的開(kāi)發(fā)語(yǔ)言還是c和c++,即便是引進(jìn)OSGI,所得的好處范圍有限。而我對(duì)松散耦合的模塊化開(kāi)發(fā)向往已久。查了一下OSGI對(duì)C++支持的好像是有一個(gè)開(kāi)源項(xiàng)目,不過(guò)好像應(yīng)用范圍很小。而SCA標(biāo)準(zhǔn)中是有對(duì)C++實(shí)現(xiàn)模型的支持的,但是幾個(gè)開(kāi)源的框架目前還只支持JAVA。

            昨天看了丁亮的轉(zhuǎn)載的一篇博客《C/C++:構(gòu)建你自己的插件框架 》,原文的鏈接:http://blog.chinaunix.net/u/12783/showart_662937.html 。看了一下里面講的方法,自己倒是可以實(shí)現(xiàn)。所以有了構(gòu)建自己的c/c++插件開(kāi)發(fā)框架的想法。今天先寫(xiě)一下初步的設(shè)想。

          C/C++插件開(kāi)發(fā)框架的要素

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

            1、如何注冊(cè)插件;

            2、如何調(diào)用插件;

            3、如何測(cè)試插件;

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

            5、插件的管理和維護(hù);

            6、插件的組裝;

            7、插件的出錯(cuò)處理;

            8、服務(wù)事件的廣播和訂閱(這個(gè)目前還沒(méi)有考慮要支持);

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

          采用的技術(shù)
            插件框架要解決的一個(gè)問(wèn)題就是插件的動(dòng)態(tài)加載能力。這里可以使用共享庫(kù)的動(dòng)態(tài)加載技術(shù)。當(dāng)然,為了簡(jiǎn)單,第一步只考慮做一個(gè)linux下的插件框架。

            總體結(jié)構(gòu)

            框架的總體結(jié)構(gòu)上,參考OSGI的“微內(nèi)核+系統(tǒng)插件+應(yīng)用插件”結(jié)構(gòu)。這里要好好考慮一下把什么做在內(nèi)核中。關(guān)于微內(nèi)核結(jié)構(gòu),以前我做個(gè)一個(gè)微內(nèi)核流程引擎,會(huì)在后面有時(shí)間和大家分享。

            框架中模塊間的數(shù)據(jù)傳送,有兩種解決方法:一是普元采用的XML數(shù)據(jù)總線的做法。優(yōu)點(diǎn)是擴(kuò)展性好,可讀性好。但是速度有些慢。二是采用我熟悉的信元流。優(yōu)點(diǎn)的效率高,訪問(wèn)方便,但是可讀性差一點(diǎn),另外跨框架的數(shù)據(jù)傳送,需要考慮網(wǎng)絡(luò)字節(jié)序的問(wèn)題。

            對(duì)于框架間的通信,通過(guò)系統(tǒng)插件封裝,對(duì)應(yīng)用插件隱藏通信細(xì)節(jié)。

                部署

                努力做到一鍵式部署。

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

          《重構(gòu)》第三章學(xué)習(xí)筆記

          我們必須培養(yǎng)自己的判斷力,來(lái)決定在什么時(shí)候進(jìn)行重構(gòu)。

          1.1  Duplicate Code(重復(fù)代碼)

          如果你在一個(gè)以上地點(diǎn)看到相同的程序結(jié)構(gòu),那么將他們合而為一會(huì)更好。

          1.2  Long Method(過(guò)長(zhǎng)函數(shù))

          擁有短函數(shù)的對(duì)象會(huì)活得比較好,比較長(zhǎng)。

          間接層所能帶來(lái)的全部益處:解釋能力(可讀性),共享能力(重用性),選擇能力(?)。

          現(xiàn)在OO 語(yǔ)言基本解決了函數(shù)調(diào)用所產(chǎn)生的開(kāi)銷。

          “ 你應(yīng)該更積極進(jìn)去的分解函數(shù)。我們遵循這樣一條原則:每當(dāng)感覺(jué)需要以注釋來(lái)說(shuō)明點(diǎn)什么的時(shí)候,我們就把需要說(shuō)明的東西寫(xiě)進(jìn)一個(gè)函數(shù)中,并以其用途(而非實(shí)現(xiàn)手法)命名。我們可以對(duì)一組甚至短短一行代碼(擁有復(fù)雜邏輯,難以理解)做這件事。哪怕替換后的函數(shù)調(diào)用動(dòng)作比函數(shù)自身還長(zhǎng),只要函數(shù)名稱能夠解釋其用途,我們也該毫不猶豫的這么做。關(guān)鍵不在于函數(shù)的長(zhǎng)度,而在于“做什么”和“如何做”之間的語(yǔ)義距離。 ”

          “如何確定該提煉哪一段代碼?一個(gè)很好的技巧是:尋找注釋。它們通常是指出“代碼用途和實(shí)現(xiàn)手法間的語(yǔ)義距離”的信號(hào)。如果代碼需要用注釋來(lái)說(shuō)明其用途,那么就要考慮把這段代碼提煉成獨(dú)立的函數(shù),并且用注釋來(lái)為此函數(shù)命名。”

          復(fù)雜條件式和循環(huán)液常常是提煉的信號(hào)。

          1.3  Large Class(過(guò)大類)

          如果想利用單一的class 做太多的事情,其內(nèi)往往會(huì)出現(xiàn)太多的 instance 變量。

          如果class 中擁有太多的代碼,也是“代碼重復(fù)、混亂、死亡”的絕佳滋生點(diǎn)。

          1.4  Long Parameter List(過(guò)長(zhǎng)的參數(shù)列表)

          過(guò)長(zhǎng)的產(chǎn)生導(dǎo)致程序難以理解。

          1.5  Divergent Change(發(fā)散式變化)

          “ 一個(gè)class 受多個(gè)外界變化的影響 ”,則把這多個(gè)變化封裝成一個(gè)新的類。即“ 將總是一起變化的東西放在一起 ”

          針對(duì)外界某一變化所有相應(yīng)的修改,都應(yīng)該只發(fā)生在單一的class 中,而這個(gè) class 的所有內(nèi)容都應(yīng)該反映該外界變化。總的思想就是,封裝變化。這個(gè)地方和設(shè)計(jì)模式的想法是一致的。

          1.6  Shotgun Surgery(散彈式修改)

          和發(fā)散式變化不同,每次遇到變化,都要在多個(gè)class 中進(jìn)行小的修改以響應(yīng)之。他們分散在多處,很容易出錯(cuò)。

          這里的主要思想是集中變化。

          散彈式修改指的是,“ 一種變化引發(fā)多個(gè)class 的修改 ”,發(fā)散式變化指的是“ 一個(gè)class 受多個(gè)外界變化的影響 ”。

          這兩種情況下,通過(guò)重構(gòu), 使“外界變化”和“待修改類”呈一對(duì)一關(guān)系 的理想境地。

          1.7  Feature Envy(依戀情節(jié))

          某個(gè)函數(shù)對(duì)其他類的數(shù)據(jù)的興趣,高過(guò)對(duì)host class 的興趣。即對(duì)其他的類的數(shù)據(jù)的依賴十分大。

          1.8  Data Clumps(數(shù)據(jù)泥團(tuán))

          數(shù)據(jù)泥團(tuán)指的是總是綁定在一起出現(xiàn)的數(shù)據(jù)。

          一個(gè)好的評(píng)斷方法:刪除眾多數(shù)據(jù)中的一項(xiàng)數(shù)據(jù),其他數(shù)據(jù)是否是因而失去了意義?如果他們不再有意義:你應(yīng)該為他們產(chǎn)生一個(gè)新的對(duì)象。

          形成新的對(duì)象后,可以根據(jù)Feature Envy 將一些操作移至此對(duì)象中。

          1.9  Primitive Obsession(基本型別偏執(zhí))

          建立多個(gè)很小,但是很靈活的對(duì)象。

          1.10  Switch Statements( switch 驚悚現(xiàn)身)

          使用面向?qū)ο缶幊蹋儆胹witch 和 case 語(yǔ)句。而是用多態(tài)來(lái)替換它。

          1.11  Parallel Inheritance Hierarchies(平行繼承體系)

          每當(dāng)你為一個(gè)class 增加一個(gè) subclass 的時(shí)候,必須為另一個(gè) class 增加一個(gè) subclass 。一般這兩個(gè) class 的前綴相同。

          1.12  Lazy Class(冗贅類)

          類顯得多余,沒(méi)有價(jià)值。

          1.13  Speculative Generality(夸夸其談未來(lái)性)

          這個(gè)往往是過(guò)度設(shè)計(jì)的結(jié)果:對(duì)某種變化的應(yīng)對(duì),而這種變化沒(méi)有發(fā)生。

          1.14  Temporary Field(令人迷惑的暫時(shí)值域)

          變量只在特定的情形下有效,而并不是所有的情況下有效。很多情況下,這些值域應(yīng)該不屬于此class ,而應(yīng)該單獨(dú)的提取成新的類。

          1.15  Message Chains(過(guò)度耦合的消息鏈)

          用戶向一個(gè)對(duì)象索取另一個(gè)對(duì)象,然后在向后者索求另一個(gè)對(duì)象,然后在索求另一個(gè)對(duì)象——客戶與查找過(guò)程的航行結(jié)構(gòu)緊密耦合。

          1.16  Middle Man(中間轉(zhuǎn)手人)

          對(duì)象的基本特征之一就是封裝——對(duì)外部世界隱藏實(shí)現(xiàn)細(xì)節(jié)——封裝往往伴隨委托。委托的過(guò)度運(yùn)行,就導(dǎo)致了Middle Man 。

          1.17  Inappropriate Intimacy (親密關(guān)系)

          兩個(gè)class 之間的關(guān)系過(guò)于親密。比如,花大量的時(shí)間探究彼此的 private 成分。

          1.18  Alternative Classes with Different Interface(異曲同工的類)

          類名不同,但是功能相似。

          1.19  Incomplete Library Class(不完美的程序類庫(kù))

          基礎(chǔ)類庫(kù)無(wú)法滿足實(shí)際的需求。

          1.20  Data Class(純稚的數(shù)據(jù)類)

          它們擁有一些值域,以及用于訪問(wèn)(讀寫(xiě))這些值域的函數(shù),除此之外一無(wú)長(zhǎng)物。

          1.21  Refused Bequest(被拒絕的遺贈(zèng))

          子類不像繼承父類的函數(shù)和數(shù)據(jù),這往往是繼承體系的錯(cuò)誤。

          如果子類復(fù)用父類的行為,但又不愿支持父類的接口,這種情況下Refused Bequest 的壞味道會(huì)很強(qiáng)烈。

          1.22  Comments(過(guò)多的注釋)

          注釋其實(shí)是一種香味,更多的情況下它被用作除臭劑:即代碼中出現(xiàn)壞味道(設(shè)計(jì)糟糕的代碼),然后用注釋“除臭”。這個(gè)時(shí)候我們應(yīng)該對(duì)這些壞味道的代碼進(jìn)行重構(gòu),然后,你會(huì)發(fā)現(xiàn)注釋變成了多余的。

          當(dāng)你感覺(jué)需要注釋,請(qǐng)先嘗試重構(gòu),試著讓所有的注釋都變得多余——代碼本身就是自注釋的。

          注釋可以用來(lái)記述“為什么做某事”、“打算做某事”、“無(wú)十足把握的區(qū)域”,而不必記錄“怎么做”。

          posted @ 2009-10-12 20:15 常高偉 閱讀(797) | 評(píng)論 (0)編輯 收藏

          以前做過(guò)一個(gè)產(chǎn)品,共分為三層:平臺(tái)層,應(yīng)用服務(wù)器層,應(yīng)用層。其中有一個(gè)業(yè)務(wù)流程,實(shí)現(xiàn)是在應(yīng)用層,但它那里的信息不全,需要通過(guò)應(yīng)用服務(wù)器層向平臺(tái)層獲取必要的業(yè)務(wù)數(shù)據(jù),然后通過(guò)應(yīng)用服務(wù)器層控制業(yè)務(wù)流程。當(dāng)時(shí)考慮這個(gè)結(jié)構(gòu)的時(shí)候,主要的出發(fā)點(diǎn)就是業(yè)務(wù)和控制分離,將業(yè)務(wù)處理從平臺(tái)層剝離開(kāi)來(lái)。當(dāng)時(shí),在具體是實(shí)施過(guò)程中,我們工程師對(duì)這種結(jié)構(gòu)抵觸心理很強(qiáng)烈。他認(rèn)為我們的業(yè)務(wù)開(kāi)發(fā)非常的繁瑣,而且經(jīng)常要貼“狗皮膏藥”。

          先拋開(kāi)上面這個(gè)實(shí)例的設(shè)計(jì)思路,這里面反映出一個(gè)問(wèn)題:軟件開(kāi)發(fā)過(guò)程中,軟件體系結(jié)構(gòu)同樣需要“重構(gòu)”

          結(jié)合經(jīng)典的《重構(gòu)》,這里簡(jiǎn)單的寫(xiě)一下軟件體系結(jié)構(gòu)重構(gòu)的定義,原因,設(shè)計(jì),方法。僅作拋磚引玉,希望能和大家一起思考。

          何謂重構(gòu)

          對(duì)軟件體系結(jié)構(gòu)的一種調(diào)整,目的是在不改變其“外在行為”的前提下,調(diào)整其結(jié)構(gòu),使其易于修改,維護(hù)和理解。

          為何重構(gòu)

          1、使整個(gè)系統(tǒng)易于添加新的功能。為系統(tǒng)添加新功能將會(huì)非常的容易。

          2、調(diào)整系統(tǒng)中各個(gè)模塊的功能,角色,使整個(gè)系統(tǒng)更容易理解。
          何時(shí)重構(gòu)

          由于系統(tǒng)結(jié)構(gòu)的重構(gòu)成本非常高,所以要選擇一個(gè)合適的重構(gòu)時(shí)機(jī)。

          1、為系統(tǒng)添加功能時(shí)重構(gòu)。此時(shí)項(xiàng)目進(jìn)度壓力如果非常大,這放棄此時(shí)重構(gòu)。

          2、軟件第一個(gè)版本開(kāi)發(fā)完畢后重構(gòu)。在第一個(gè)版本開(kāi)發(fā)完畢,第二個(gè)版本開(kāi)發(fā)之前,根據(jù)第一個(gè)版本的開(kāi)發(fā)經(jīng)驗(yàn),對(duì)系統(tǒng)進(jìn)行重構(gòu)。

          3、開(kāi)發(fā)出系統(tǒng)原型時(shí)進(jìn)行重構(gòu)。開(kāi)發(fā)出一個(gè)系統(tǒng)的原型的時(shí)候,如果發(fā)現(xiàn)系統(tǒng)需要重構(gòu),這及時(shí)的進(jìn)行,這個(gè)時(shí)候重構(gòu)成本較低,但對(duì)是否重構(gòu)決策要求較高。

          重構(gòu)的必要條件

          重構(gòu)之前必須為軟件系統(tǒng)建立一個(gè)可靠的、自動(dòng)化的功能測(cè)試環(huán)境,這樣才能有效防止重構(gòu)帶來(lái)的危害。好的測(cè)試時(shí)重構(gòu)的根本。重構(gòu)之前,首先檢查自己是否有一套可靠的測(cè)試機(jī)制。這些測(cè)試必須有自我檢驗(yàn)(selfchecking)能力。

          重構(gòu)與設(shè)計(jì)

          系統(tǒng)架構(gòu)層次的重構(gòu),因?yàn)橹貥?gòu)的成本相對(duì)較高,所以預(yù)先設(shè)計(jì)的程度要相對(duì)較深,要盡量考慮系統(tǒng)可能遇到的情況,方案要適當(dāng)?shù)撵`活和強(qiáng)固。盡量減少系統(tǒng)結(jié)構(gòu)的重構(gòu)。

          軟件體系結(jié)構(gòu)的重和代碼的重構(gòu)的區(qū)別

          1、針對(duì)的層次不同:一個(gè)是系統(tǒng)結(jié)構(gòu)層次的,一個(gè)是代碼層次的。

          2、重構(gòu)成本不同,系統(tǒng)結(jié)構(gòu)的重構(gòu)成本相對(duì)較高。

          3、是否重構(gòu)的決策者不同。

          4、重構(gòu)的時(shí)機(jī)不同。

          posted @ 2009-10-12 20:14 常高偉 閱讀(352) | 評(píng)論 (0)編輯 收藏

          在學(xué)習(xí)的不同的階段我們應(yīng)該保持的心態(tài):
          1、接觸之前,擁有一個(gè)好奇的心態(tài)。與自己原有的知識(shí)對(duì)比,不要對(duì)新的知識(shí)產(chǎn)生偏見(jiàn)。比如,原來(lái)一直采用瀑布式軟件開(kāi)發(fā),在接觸敏捷軟件開(kāi)發(fā)之前,不要對(duì)敏捷軟件開(kāi)發(fā)模式產(chǎn)生偏見(jiàn):“敏捷開(kāi)發(fā)根本不能夠和瀑布式開(kāi)發(fā)相提并論”。偏見(jiàn)會(huì)阻礙新知識(shí)的學(xué)習(xí),而是要對(duì)敏捷開(kāi)發(fā)保持一種好奇心:

          1)這種理論為什么會(huì)提出來(lái)?
          2)主要為了解決什么問(wèn)題?
          3)現(xiàn)有的理論無(wú)法解決此問(wèn)題嗎?
          4)它是基于什么樣的原理?
          5)它是如何運(yùn)行的?

          2、學(xué)習(xí)了一段時(shí)間,擁有一定基礎(chǔ)之后,要以一種批判的、懷疑的心態(tài)來(lái)對(duì)待學(xué)習(xí)的知識(shí)。當(dāng)你擁有了對(duì)新知識(shí)的一定認(rèn)識(shí)后,有可能會(huì)發(fā)現(xiàn)它很好解決了你以前遇到的一些非常棘手的問(wèn)題,或者發(fā)現(xiàn)你找到了某一領(lǐng)域問(wèn)題的完美的解決方案。這個(gè)時(shí)候,你要讓自己堅(jiān)信,“沒(méi)有銀彈”,沒(méi)有包治百病的靈丹妙藥。任何方案、理論、技術(shù)都有其依賴的條件,或者在解決原有問(wèn)題后,引入的新的問(wèn)題。此時(shí),你要以一種批判的、懷疑的態(tài)度來(lái)對(duì)待它。要搞清楚:

          1)它所依賴的前提條件是什么?
          2)應(yīng)用它的時(shí)候有沒(méi)有什么假設(shè)?
          3)它在那些場(chǎng)景下適用、那些場(chǎng)景下不適用?
          4)它在解決原有問(wèn)題之后,有沒(méi)有引入新的問(wèn)題?

          3、當(dāng)你達(dá)到專家級(jí)或頂級(jí)水平之后,應(yīng)該持有一種“獨(dú)孤求敗”的心態(tài):尋求新的挑戰(zhàn),爭(zhēng)取“百尺竿頭更進(jìn)一步”,就像獨(dú)孤求敗一樣,”闖蕩江湖,只求一敗 “:把每次的失敗看成進(jìn)步的機(jī)會(huì)。同時(shí)保持敬畏、謙虛的心態(tài),你所掌握的知識(shí),也不是”銀彈“,也有其局限之處,或者隨著環(huán)境的發(fā)展,已經(jīng)無(wú)法適應(yīng)環(huán)境,需要尋求新的突破。這個(gè)時(shí)候,你要根據(jù)你豐富的實(shí)踐,提出創(chuàng)新的思路或知識(shí)。
          總之:
          1、在學(xué)習(xí)新知識(shí)、新領(lǐng)域的時(shí)候,要保持一個(gè)空杯的心態(tài),只有這樣才能為自己敞開(kāi)自我提升之路。
          2、偏見(jiàn)導(dǎo)致對(duì)新知識(shí)的排斥,教條倒是對(duì)新知識(shí)的盲從。
          3、沒(méi)有絕對(duì)的真理,也沒(méi)有絕對(duì)的謬論。絕對(duì)的真理讓我們盲從,絕對(duì)的謬論讓我們偏見(jiàn)。

          posted @ 2009-10-12 20:13 常高偉 閱讀(137) | 評(píng)論 (0)編輯 收藏

          1  構(gòu)筑測(cè)試體系

          如果你想進(jìn)行重構(gòu),首要前提就是要擁有一個(gè)可靠的測(cè)試環(huán)境。

          “編寫(xiě)優(yōu)良的測(cè)試程序,可以極大的提高我的編程速度,即使不進(jìn)行重構(gòu)也是如此。”

          1.1  自我測(cè)試代碼(Self-testing Code )的價(jià)值

          “Class 應(yīng)該包含他們自己的測(cè)試代碼。”

          “每個(gè)Class 都有一個(gè)測(cè)試函數(shù),并用它測(cè)試自己這個(gè) Class 。”

          確保所有的測(cè)試都完全自動(dòng)化,讓它們檢查自己的測(cè)試結(jié)果。

          只要寫(xiě)好一點(diǎn)功能,就立即添加測(cè)試。

          一整組(a suite of )測(cè)試就是一個(gè)強(qiáng)大的“臭蟲(chóng)”偵測(cè)器,能夠大大縮減查找“臭蟲(chóng)”所需要的時(shí)間。

          “實(shí)際上,編寫(xiě)測(cè)試代碼的最有用時(shí)機(jī)是在開(kāi)始編程之前。當(dāng)你需要添加特性的時(shí)候,先寫(xiě)相應(yīng)的測(cè)試代碼。聽(tīng)起來(lái)離經(jīng)叛道,其實(shí)不然。填寫(xiě)測(cè)試代碼其實(shí)就是問(wèn)自己:添加這個(gè)功能需要做什么。編寫(xiě)測(cè)試代碼還能使你把注意力集中于接口而非實(shí)現(xiàn)上頭(永遠(yuǎn)是件好事)。預(yù)先寫(xiě)好的測(cè)試代碼也為你的工作按上一個(gè)明確的結(jié)束標(biāo)志:一旦測(cè)試代碼運(yùn)行正常,工作就可以結(jié)束了。”

          構(gòu)建自我測(cè)試的代碼。

          1.2  JUnit測(cè)試框架( Testing Framew )

          頻繁的運(yùn)行測(cè)試,每次編譯請(qǐng)把測(cè)試也考慮進(jìn)去,每天至少執(zhí)行每個(gè)測(cè)試一次。

          單元測(cè)試和功能測(cè)試

          “每當(dāng)你接獲臭蟲(chóng)提報(bào),請(qǐng)先撰寫(xiě)一個(gè)單元測(cè)試來(lái)揭發(fā)這只臭蟲(chóng)。”——如何揭發(fā)?這里需要根據(jù)報(bào)告準(zhǔn)確定位。單元測(cè)試會(huì)對(duì)此有幫助嗎?

          1.3  添加更多的測(cè)試

          “觀察Class 該做的所有事情,然后針對(duì)任何一項(xiàng)功能的任何一種可能失敗的情況,進(jìn)行測(cè)試。”

          “測(cè)試應(yīng)該是一種風(fēng)險(xiǎn)驅(qū)動(dòng)(risk driven )行為,測(cè)試的目的是希望找出現(xiàn)在或未來(lái)的可能出現(xiàn)的錯(cuò)誤。”

          “測(cè)試的訣竅是:測(cè)試你最擔(dān)心的部分。”

          這點(diǎn)和我目前的想法不大相同。我目前的想法是,測(cè)試要對(duì)程序做100% 的保證,所以,要測(cè)試程序可能行為的每一種情況,保證其正確性。按照我的想法,值域的設(shè)置和訪問(wèn)函數(shù)也是要測(cè)試的。作者的意思是,測(cè)試代碼要用最低的成本,獲取最大的收益。這一點(diǎn),要我在實(shí)際的環(huán)境中進(jìn)行抉擇。

          “編寫(xiě)不是十分完美的測(cè)試并實(shí)際運(yùn)行,好過(guò)對(duì)完美測(cè)試的無(wú)盡等待。”——我持懷疑態(tài)度。

          運(yùn)用測(cè)試用例前后執(zhí)行的函數(shù):tearDown 和 setUp ,保證測(cè)試用例之間相互隔離,而非相互影響。

          做一個(gè)懶惰的程序員——。

          考慮可能出錯(cuò)的邊界條件,把測(cè)試火力集中在那兒。

          “測(cè)試(優(yōu)先)可以調(diào)高編程速度”,這一點(diǎn)我要在實(shí)踐中驗(yàn)證一下,如果真是這樣,那我就要嘗試在我們部門(mén)推行這種方法。

          “當(dāng)測(cè)試達(dá)到一定的程度后,測(cè)試效益會(huì)呈現(xiàn)遞減態(tài)勢(shì)。”所以,你不要期望通過(guò)測(cè)試找出所有的bug ,而是要通過(guò)測(cè)試,找出絕大多數(shù)的 bug 。

          這個(gè)地方其實(shí)也符合“二八定律”:即20% 的測(cè)試可以找出 80% 的 bug ,其余的 80% 的測(cè)試可以找出剩下的 20% 的 bug 。我們要做的,就是寫(xiě)這 20% 的測(cè)試,而非 100% 的測(cè)試。

          posted @ 2009-10-12 20:11 常高偉 閱讀(150) | 評(píng)論 (0)編輯 收藏

          上一篇介紹了微內(nèi)核流程引擎開(kāi)發(fā)背景,這篇介紹它的功能描述。

          基本功能:

          1、能夠通過(guò)腳本定義流程,更改流程。

          2、對(duì)軟交換系統(tǒng)應(yīng)用服務(wù)器的所有的接口都可以編輯。

          3、異常處理,實(shí)現(xiàn)補(bǔ)償機(jī)制。

          4、流程要支持:順序執(zhí)行,分支處理,跳轉(zhuǎn)執(zhí)行。

          5、腳本中支持簡(jiǎn)單的數(shù)據(jù)庫(kù)操作,比如:記錄查詢(根據(jù)查詢結(jié)果決定流程),字段查詢,記錄增刪改。


          擴(kuò)展功能:

          1、提供多種調(diào)用形式:1)動(dòng)態(tài)鏈接庫(kù)直接調(diào)用;2)socket通信調(diào)用;3)遠(yuǎn)程調(diào)用;4)WSDL方式調(diào)用。

          2、實(shí)現(xiàn)一個(gè)流程引擎虛擬機(jī)。專門(mén)處理流程。

          3、支持業(yè)務(wù)以無(wú)狀態(tài)的形式開(kāi)發(fā)。所有的狀態(tài)在腳本中定義。

          4、開(kāi)發(fā)一個(gè)流程編輯界面。

          5、開(kāi)發(fā)一個(gè)腳本編譯器,檢查腳本的錯(cuò)誤。

          6、開(kāi)發(fā)一個(gè)簡(jiǎn)單的語(yǔ)言,實(shí)現(xiàn)快速流程編輯的功能。這里要實(shí)現(xiàn)一個(gè)編譯器,編譯結(jié)果就是流程腳本。

          7、實(shí)現(xiàn)一個(gè)方向編譯器,從流程腳本到流程開(kāi)發(fā)語(yǔ)言。

          上面的這些功能有的已經(jīng)實(shí)現(xiàn),有的正在實(shí)現(xiàn)。后面我會(huì)詳細(xì)描述這些功能的設(shè)計(jì)與實(shí)現(xiàn)。

          posted @ 2009-10-12 20:08 常高偉 閱讀(252) | 評(píng)論 (0)編輯 收藏

          我設(shè)計(jì)的流程引擎是腳步驅(qū)動(dòng)的。腳本中定義了流程執(zhí)行的環(huán)境,流程操作的對(duì)象,流程執(zhí)行的步驟。下面是一個(gè)流程腳本的示例:

          <?xml version="1.0" encoding="utf-8"?>
          <process name="make_call">
              <data type="user_tel">called_number</data>
               <object type="user" id="global_data:called_number" operation="must">obj_user</object>
               //用戶對(duì)象描述中,號(hào)碼是必須的,是流程引擎和業(yè)務(wù)的交互唯一標(biāo)識(shí),callid是可選的。
              <object type="user" object_num="global_data:called_number" operation="must">obj_user</object>
              <sequence name="make_call">
                  <invoke interface="make_call" node="make_call_001" object_user="obj_user" calling_number="6699" original_number="123456" call_type="local_call">

                  </invoke>
                  <invoke interface="play_voice" node="play_voice_001" object_user="obj_user" play_long="100" play_file="/home/welcome.au">

                   </invoke>   
              </sequence>
          </process>


          腳本的含義

          1、process的name屬性表示流程的名稱,用于在程序中調(diào)用。

          2、<data type="user_tel">called_number</data>表示定義了一個(gè)流程的全局外部變量。有程序在調(diào)用流程是作為流程數(shù)據(jù)傳送給流程。這個(gè)數(shù)據(jù)要在后面的流程中使用。

          3、<object>部分在流程中定義流程操作的對(duì)象。一般分為用戶和會(huì)場(chǎng)。這里表示是用戶。屬性“id”表示對(duì)象的唯一標(biāo)識(shí)。這里引用的是流程的全局?jǐn)?shù)據(jù):global_data:called_number,也就是在上面定義的數(shù)據(jù)。屬性“operation”表示此對(duì)象是可選還是必選。如果是必須,這如果此對(duì)象被釋放,這流程也要被被結(jié)束。否則,不結(jié)束。中間的內(nèi)容表示對(duì)象在流程中的唯一標(biāo)示,這里是obj_user,后面的節(jié)點(diǎn)可以通過(guò)使用它來(lái)操作對(duì)象。

          4、<sequence>表示順序調(diào)用下面的節(jié)點(diǎn)。

          5、<invoke >表示調(diào)用節(jié)點(diǎn)。屬性“interface="make_call"”表示此節(jié)點(diǎn)調(diào)用的接口是make_call。make_call是在代碼中定義好的關(guān)鍵字,對(duì)應(yīng)一個(gè)軟交換系統(tǒng)的接口。屬性“node”表示節(jié)點(diǎn)的唯一標(biāo)識(shí),在流程內(nèi)部唯一,可以在流程跳轉(zhuǎn)的時(shí)候使用。 “object_user="obj_user"“表示make_call 接口操作的對(duì)象。有<object>創(chuàng)建。 calling_number="6699" original_number="123456" call_type="local_call"表示的是make_call接口調(diào)用時(shí)的數(shù)據(jù)。

          6、<invoke interface="play_voice"表示對(duì)此對(duì)象進(jìn)行放音。

          這個(gè)腳本的意思是,根據(jù)流程輸入的號(hào)碼,創(chuàng)建用戶對(duì)象,并且發(fā)起呼叫,對(duì)用戶進(jìn)行放音。


          復(fù)雜的腳步定義:

          上面的是一個(gè)簡(jiǎn)單的示例。為了能夠?qū)崿F(xiàn)流程編輯,要考慮很多的情況,要能夠進(jìn)行分支處理,跳轉(zhuǎn)執(zhí)行,捕獲事件等。

          1、分支的實(shí)現(xiàn)

              <recive event="user_key" ="" node="receive_key" object_user="obj_user" time_out="10"></recive>
              <switch condition_type="user_key" object="obj_user">
                      <case condition="9092">
                          <sequence name="d">
                          </sequence>
                      </case>
                      <case condition="time_out">
                          <sequence name="d">
                          </sequence>
                      </case>           
                      <otherwise>
                            <sequence name="">
                                <goto node="play_voice_001">goto_001</goto>
                            </sequence>
                      </otherwise>
              </switch>

          1)<recive event="user_key"表示接受指定用戶的按鍵。如果超過(guò)10秒為收到按鍵則認(rèn)為用戶按鍵結(jié)束。

          2)<switch condition_type="user_key"表示一用戶的按鍵為分支條件,進(jìn)行分支處理。

          3)<case condition="9092">表示如果用戶的按鍵式0092的話則進(jìn)入此分支進(jìn)行處理。

          4)<case condition="time_out">如果超時(shí)為收到用戶按鍵,這進(jìn)入此分支處理

          5)<otherwise>如果上面的條件都不滿足,則進(jìn)入此分支處理。


          2、跳轉(zhuǎn)的實(shí)現(xiàn):

          <goto node="goto_001" next_node="play_voice_001"></goto>

          表示此節(jié)點(diǎn)是一個(gè)跳轉(zhuǎn)節(jié)點(diǎn),要跳轉(zhuǎn)到的下一個(gè)節(jié)點(diǎn)是play_voice_001。


          3、信號(hào)捕獲的實(shí)現(xiàn):

                 <pick name="pick_001" time_out="10">
                      <on_event event="on_ring_180" result="success" reason="normal">
                          <sequence name="008">
                          </sequence>
                      </on_event>
                      <time_out>
                          <sequence name="008">
                          </sequence>
                      </time_out>
                      <otherwise event="on_ring_180:on_ring_183">
                            <sequence name="009">
                              </sequence>
                      </otherwise>
                  </pick>

          1)<pick name="pick_001" time_out="10"><pick>活動(dòng)會(huì)等待一組相互排斥事件中的一個(gè)事件的發(fā)生,然后執(zhí)行與發(fā)生的事件相關(guān)聯(lián)的活動(dòng)。它會(huì)阻塞業(yè)務(wù)流程執(zhí)行,以等待某一特定的事件發(fā)生,比如接收到一個(gè)合適的消息或超時(shí)警報(bào)響起。當(dāng)其中任何一個(gè)事件被觸發(fā)后,業(yè)務(wù)流程就會(huì)繼續(xù)執(zhí)行,pick也隨即完成了,不會(huì)再等待其他事件的發(fā)生。

          2)<on_event event="on_ring_180" result="success" reason="normal">表示如果收到的on_ring_180,且結(jié)果是success,原因是normal。觸發(fā)此流程的處理。

          3)<time_out>表示超時(shí)為收到制定事件的處理。

          4)<otherwise event="on_ring_180:on_ring_183">表示收到其他的事件,比如:on_ring_180或on_ring_183,都進(jìn)入此分支處理。

          posted @ 2009-10-12 20:08 常高偉 閱讀(202) | 評(píng)論 (0)編輯 收藏

          開(kāi)發(fā)背景

          我們公司是主要從事企業(yè)語(yǔ)音方面產(chǎn)品的開(kāi)發(fā),主要產(chǎn)品比如:調(diào)度系統(tǒng),指揮系統(tǒng),電話會(huì)議系統(tǒng),呼叫中心系統(tǒng)等。這些系統(tǒng)都有一個(gè)共同特點(diǎn),就是涉及到呼叫,放音,收發(fā)按鍵,會(huì)場(chǎng)操作。我們的業(yè)務(wù)產(chǎn)品都是基于我們的軟交換系統(tǒng)之上構(gòu)建的,軟交換系統(tǒng)的應(yīng)用服務(wù)器向外提供這些服務(wù)。


          產(chǎn)生的問(wèn)題

          我們?cè)陂_(kāi)發(fā)的過(guò)程中就發(fā)現(xiàn)一個(gè)問(wèn)題,每個(gè)產(chǎn)品在此接口上都會(huì)做很多重復(fù)的開(kāi)發(fā),特別是在IVR處理上面。

          IVR (Interactive Voice Response)即交互式語(yǔ)音應(yīng)答,可以提高呼叫服務(wù)的質(zhì)量并節(jié)省費(fèi)用。IVR是一種功能強(qiáng)大的電話自動(dòng)服務(wù)系統(tǒng)。

          由于我們的系統(tǒng)重新架構(gòu),正在開(kāi)發(fā)軟交換API接口,所以我們?cè)噲D解決這個(gè)問(wèn)題。

          為了方便上層對(duì)底層接口的調(diào)用,減少重復(fù)開(kāi)發(fā),產(chǎn)品能夠針對(duì)用戶需求迅速更改,我們提出了事務(wù)流的概念。即上層程序設(shè)定一個(gè)事務(wù)流,比如:相對(duì)用戶9092發(fā)起呼叫,用戶應(yīng)答后對(duì)用戶放音,讓用戶輸入密碼,密碼驗(yàn)證成功加入制定會(huì)場(chǎng)。上層程序設(shè)定好這樣的事務(wù)流后,發(fā)送給下層進(jìn)行處理。下層處理完畢后,回復(fù)處理結(jié)果。

          事務(wù)流的概念最終并沒(méi)有實(shí)現(xiàn),一是我們對(duì)事務(wù)流的異常處理分析不足;二是采用事務(wù)流的概念擴(kuò)展性,可維護(hù)性不是很好。

          后來(lái)我們?cè)谑聞?wù)流的基礎(chǔ)上引進(jìn)了SOA的概念,即將底層的接口封裝為一系列松散耦合的服務(wù),在上層通過(guò)對(duì)服務(wù)的編排實(shí)現(xiàn)流程編輯的功能。但是實(shí)施SOA困難非常大,一是SOA沒(méi)有大規(guī)模的應(yīng)用,文檔,經(jīng)驗(yàn)都比較少,實(shí)施風(fēng)險(xiǎn)較大;二是實(shí)施 SOA要工具的支持,需要投資。這樣的話我們寧可重復(fù)開(kāi)發(fā)。

          最終這個(gè)流程編輯的功能并沒(méi)有完成,但是我們還是在API中實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的流程編輯,這個(gè)流程編輯是通過(guò)硬編碼來(lái)實(shí)現(xiàn)的,和我們的愿望相差較大。


          新的思路

          后來(lái)看了一家平臺(tái)軟件公司的介紹,他們是做企業(yè)應(yīng)用平臺(tái)的。企業(yè)應(yīng)用中很多的一點(diǎn)就是工作流。他們的平臺(tái)就是集成了一款開(kāi)源的工作流引擎:JBPM。第一次接觸JBPM,感覺(jué)很多思路可以借鑒。后來(lái)在網(wǎng)上搜資料的時(shí)候搜到幾篇研究微內(nèi)核流程引擎的文章:

          揭秘jbpm流程引擎內(nèi)核設(shè)計(jì)思想及構(gòu)架

          微內(nèi)核過(guò)程引擎的設(shè)計(jì)思路和構(gòu)架

          微內(nèi)核工作流引擎體系架構(gòu)與部分解決方案參考

          這幾篇文章是我設(shè)計(jì)流程引擎的核心基礎(chǔ)。看完之后最終決定自己設(shè)計(jì)一個(gè)微內(nèi)核流程引擎。


          使用開(kāi)源還是自己開(kāi)發(fā)

          決定動(dòng)手開(kāi)發(fā)前,有兩種方式,一是使用開(kāi)源產(chǎn)品,比如JBPM;二是自己動(dòng)手開(kāi)發(fā)。當(dāng)時(shí)的考慮是:

          1、JBPM是用java開(kāi)發(fā),我們公司的業(yè)務(wù)產(chǎn)品目前基本上都是c,和java交互不便。

          2、JBPM是針對(duì)企業(yè)工作流設(shè)計(jì)的,工作流和IVR導(dǎo)航一個(gè)重要的區(qū)別是,IVR導(dǎo)航對(duì)時(shí)效性要求很高。要求系統(tǒng)能夠及時(shí)響應(yīng)。

          3、我不是JAVA出身,當(dāng)時(shí)對(duì)java還有些畏難心理。

          所以,最終決定自己用C++開(kāi)發(fā)流程引擎。

          如果是現(xiàn)在的話,即便是決定自己開(kāi)發(fā),一會(huì)好好研究一下JBPM的源碼的。


          借鑒的其他思路

          1、數(shù)據(jù)驅(qū)動(dòng)(DD)。在腳本中XML定義流程,程序啟動(dòng)后將流程讀入程序,然后供上層程序調(diào)用。如果流程更改,這直接修改腳本即可。

          2、BPEL。Business Process Execution Language 的縮寫(xiě),意為業(yè)務(wù)過(guò)程執(zhí)行語(yǔ)言 ,是一種基于XML 的,用來(lái)描寫(xiě)業(yè)務(wù)過(guò)程的編程語(yǔ)言。我的腳步語(yǔ)言參考BPEL的描述。

          posted @ 2009-10-12 20:05 常高偉 閱讀(378) | 評(píng)論 (0)編輯 收藏


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

          Copyright © 常高偉

          主站蜘蛛池模板: 临清市| 东乌| 峨边| 西丰县| 南靖县| 南川市| 鹤山市| 思茅市| 白城市| 焦作市| 佛坪县| 健康| 泰顺县| 周口市| 潞城市| 清苑县| 延庆县| 贵德县| 凤庆县| 都匀市| 丹棱县| 进贤县| 龙井市| 凉城县| 喀喇沁旗| 肃北| 色达县| 临沂市| 武威市| 綦江县| 沁源县| 溆浦县| 鹤岗市| 平远县| 门头沟区| 南靖县| 荔浦县| 托克逊县| 安化县| 普格县| 石渠县|