07.Javascript設計模式之組合模式----Composite
組合模式是一種專為建立WEB上的動態使用者介面而量身定製的模式。使用這種模式,可以用一條命令在多個對象上激發複雜的或遞迴的行為,這可以簡化粘合性代碼,使其更容易維護,而那些複雜行為則被委託給各個對象。
本文主要通過一個Windows檔案目錄結構的例子來闡述組合模式。
問題引入
Windows檔案目錄結構就像一棵樹,有根節點(我的電腦),然後有子節點(如C盤、D盤等),子節點也還有子節點(盤符下的檔案夾),檔案夾下還可能會有檔案夾,也可能會有單獨的檔案,於是,在樹的概念中,
我們稱單獨的檔案節點為葉子,而檔案夾則為樹枝。
於是,這一次有這樣一個命題,我們希望能夠有選擇地隱藏或顯示Windows目錄結構中的特定部分。這可能是單獨的檔案,也可能是檔案夾。考慮到Windows檔案目錄結構的複雜性,這裡我們只關注檔案或檔案夾的顯示和隱藏操作。
組合模式 類的定義
針對上面的命題,我們需要設計兩個類,一個用作檔案夾的組合對象類,一個用作檔案本身的葉子物件類。
//檔案夾介面:組合對象var IDirectory = new Interface("IDirectory",["add","delete","getFile"]);//檔案介面:葉子物件var IFile = new Interface("IFile",["show","hide"]);
接下來就是非常關鍵的了,這裡即將定義一個動態檔案系統類DynamicFileSystem,該類是IDirectory和IFile的實作類別。也就是說,通過DynamicFileSystem類執行個體化出來的對象,既可能是一個檔案,也可能是一個檔案夾。
其代碼如下:
//檔案系統類var DynamicFileSystem = function(id,fileName){ this.files = []; //子檔案清單(注意,檔案夾也是一個檔案) this.createDate = new Date();//檔案的建立日期 this.fileName = fileName; //檔案名稱 //為了在WEB中很好的來描述這個問題,我們需要用到HTMLDOMElement this.element = document.createElement("div"); this.element.id = id;};//實現兩個介面implements(DynamicFileSystem,IDirectory,IFile);//實現所有的抽象方法DynamicFileSystem.prototype = { add : function(fileSystemChild){ this.files.push(fileSystemChild); this.element.appendChild(fileSystemChild.getElement()); }, delete : function(fileSystemChild){ for(var file,i = 0;node = this.getFile(i),i++){ if(file == fileSystemChild){ this.files.splice(i,1); break; } } this.element.removeChild(fileSystemChild.getElement()); }, getFile : function(index){ return this.files[index]; }, show : function(){ //顯示檔案或檔案夾 this.element.style.display = "block"; for(var file,i = 0;node = this.getFile(i),i++){ file.show(); //遞迴調用 } }, hide : function(){ for(var file,i = 0;node = this.getFile(i),i++){ file.hide(); //遞迴調用 } //隱藏檔案或檔案夾 this.element.style.display = "none"; }, //拓展方法 getElement : function(){ return this.element; }};
在上面的代碼中,首先定義的是組合對象類和葉子物件類應該實現的介面。除了常規的組合對象方法外,這些類要定義的操作只包含hide和show。
接下來定義的是組合對象類。由於DynamicFileSystem只是對div對象的封裝,所以檔案系統可以再嵌套子檔案系統,而我們因此也只需要用到一個組合對象類即可。
執行個體化示範
下面就是大家迫不及待想要看到的,如何執行個體化並操作整個Windows的分類樹結構呢?
其實到這裡還沒有完,我們必須要有一個真正的檔案實體類,比如Word文檔、PDF文檔、Text文檔等等,它們都是File,因此我們可以隨便的定義一個檔案類,並讓它繼承DynamicFileSystem類即可(因為它同樣具備檔案系統的功能),
下面以Text文檔為例,建立TextFile類:
var TextFile = function(id,fileName){ //檔案名稱 this.fileName = fileName; this.element = document.createElement("div"); this.element.id = id;};//實施繼承inherits(TextFile,DynamicFileSystem);
如此一來,TextFile類就已經具備了DynamicFileSystem類的所有方法,而且還有一個自己獨特的構造器,接收一個參數----檔案名稱。
接下來就是真正的開始應用了,我們首先建立一個檔案系統(檔案夾)的執行個體,再建立三個文字檔TextFile類的執行個體,最後將這三個文字檔添加到檔案系統中去:
//建立檔案夾var fileSystem = new DynamicFileSystem("dirRoot","C盤");//建立文字檔var txtFile1 = new TextFile("txtFile1","文字檔1");var txtFile2 = new TextFile("txtFile2","文字檔2");var txtFile3 = new TextFile("txtFile3","文字檔3");//將三個文字檔添加到檔案夾中fileSystem.add(txtFile1);fileSystem.add(txtFile2);fileSystem.add(txtFile3);
在這個時候你可能想說了,我想在C盤下建立兩個檔案夾,分別是“Windows”和“Program Files”,該怎麼辦呢?很簡單,繼續之前的代碼:
var windowsDir = new DynamicFileSystem("dirWindows","Windows"); //Windows檔案夾var programDir = new DynamicFileSystem("dirProgram","Program Files"); //Program Files檔案夾//添加到C盤fileSystem.add(windowsDir);fileSystem.add(programDir);
OK,到了這一步,一切都順利的完成了。
如果你是一個愛問的同學,你想知道,如果我突然需要安裝一個軟體,該軟體的安裝目錄必須為C:Program FilesMicrosoft,安裝完成後還會在Microsoft目錄下產生一個microsoft.exe的檔案,又該怎麼類比呢?
照貓畫虎:
第一步,建立ExeFile類第二步,建立Microsoft目錄第三步,將其添加到C:/Program Files下第四步,建立ExeFile類的執行個體microsoft.exe檔案第五步,將microsoft.exe添加到C:/Program Files/Microsoft目錄下,完成
總結
如果應用得當,組合模式將是一種非常管用的設計模式。它把一批子物件組織為樹形結構,只要一條命令就可以操作樹上的所有對象,提高了代碼的模組化程度,而且便於代碼重構和對象的更換。這種模式特別適合於動態HTML使用者介面,在它的協助下,你可以在不知道使用者介面的最終格局的情況下進行開發。對於我們每一個前端Javascript程式員來說,能掌握好組合模式,一定會大受益處滴。
本文參考《Javascript設計模式》【Ross Harmes,Dustin Diaz】
小生愚昧,文中如有闡述之不當,還請不要介懷