組合模式一般是將一系列類似的小對象組合成大的對象,由這個大對象提供接口來對里面的小對象進行操作,下買呢我們來詳細(xì)看一下設(shè)計模式中的組合模式在JavaScript程序構(gòu)建中的使用
定義
組合,顧名思義是指用包含多個部件的對象創(chuàng)建單一實體。 這個單一實體將用作所有這些部件的訪問點,雖然這大大簡化了操作,但也可能具有相當(dāng)?shù)钠垓_性,因為沒有哪種隱性方式明確表明該組合包含多少部件。
組合模式的目標(biāo)是解耦客戶程序與復(fù)雜元素內(nèi)部架構(gòu),使得客戶程序?qū)Υ凶釉囟家灰曂省?/P>
每個子節(jié)點都可以使復(fù)雜的存在,對于父節(jié)點來說,不需要知道子節(jié)點的復(fù)雜性或者實現(xiàn)子節(jié)點的復(fù)雜性,只需要關(guān)注子節(jié)點的特定方法,便可以使用子節(jié)點。簡化了父和子之間的關(guān)系。
對于子節(jié)點來說也是一樣的,過多的接口暴露有時候也是一種濫用,同時也減少了對外部的依賴。
示例
我們最好使用例證解說組合。 在下圖中,您可以看到兩種不同類型的對象: 容器和庫是組合,圖像是葉片。 組合可承載子項,但一般不會實施更多行為。 葉片包含絕大多數(shù)行為,但不能承載子項,至少在傳統(tǒng)的組合示例中不可以。
此示例創(chuàng)建圖片庫,將其作為組合模式示例。 只有三個層次: 專輯、庫和圖像。 專輯和庫將作為組合,圖像是葉片,如上面那張圖所示。這是一種比組合本身需求更加明確的結(jié)構(gòu),但對于本示例而言,將這些層次僅限制為組合或葉片很有意義。 標(biāo)準(zhǔn)組合不會限制哪些結(jié)構(gòu)層次可以具有葉片,也不會限制葉片數(shù)量。
要開始操作,應(yīng)首先創(chuàng)建用于專輯和庫的 GalleryComposite“類”。 請注意,我正在使用 jQuery 執(zhí)行 DOM 操作以簡化過程。
var GalleryComposite = function (heading, id) {
this.children = [];
this.element = $('<div id="' + id + '" class="composite-gallery"></div>')
.append('<h2>' + heading + '</h2>');
}
GalleryComposite.prototype = {
add: function (child) {
this.children.push(child);
this.element.append(child.getElement());
},
remove: function (child) {
for (var node, i = 0; node = this.getChild(i); i++) {
if (node == child) {
this.children.splice(i, 1);
this.element.detach(child.getElement());
return true;
}
if (node.remove(child)) {
return true;
}
}
return false;
},
getChild: function (i) {
return this.children[i];
},
hide: function () {
for (var node, i = 0; node = this.getChild(i); i++) {
node.hide();
}
this.element.hide(0);
},
show: function () {
for (var node, i = 0; node = this.getChild(i); i++) {
node.show();
}
this.element.show(0);
},
getElement: function () {
return this.element;
}
}
這個位置有點棘手,能否允許我再更多的解釋一下? 我們同時使用 add, remove, 和getChild getChild 方法構(gòu)建這一組合。 本示例不會實際使用 remove 和 getChild,但它們對于創(chuàng)建動態(tài)組合非常有用。 hide, show, 和getElement 方法則用來操縱 DOM。 該組合旨在作為庫的 表示在頁面上向用戶展示。 該組合可通過 hide 和 show控制這些庫元素。 如果在專輯上調(diào)用 hide,則整個專輯將消失,或者您也可以只在單一圖像上調(diào)用它,這樣只有該圖像會消失。
現(xiàn)在,創(chuàng)建一個 GalleryImage類。 請注意,它使用的方法與 GalleryComposite完全相同。 換句話說,它們實現(xiàn)同一接口,不同的是該圖像是葉片,因此不會實際對子項相關(guān)方法執(zhí)行任何操作,就像不具有任何子項一樣。 必須使用同一接口運行該組合,因為組合元素不知道自身添加的是另一個組合元素還是葉片,因此如果嘗試在其子項上調(diào)用這些方法,則需要運行完全正常,沒有任何錯誤。
var GalleryImage = function (src, id) {
this.children = [];
this.element = $('<img />')
.attr('id', id)
.attr('src', src);
}
GalleryImage.prototype = {
// Due to this being a leaf, it doesn't use these methods,
// but must implement them to count as implementing the
// Composite interface
add: function () { },
remove: function () { },
getChild: function () { },
hide: function () {
this.element.hide(0);
},
show: function () {
this.element.show(0);
},
getElement: function () {
return this.element;
}
}
鑒于您已經(jīng)構(gòu)建了對象原型,您現(xiàn)已能夠進行使用。 從下面您可以看到實際構(gòu)建圖像庫的代碼。
var container = new GalleryComposite('', 'allgalleries');
var gallery1 = new GalleryComposite('Gallery 1', 'gallery1');
var gallery2 = new GalleryComposite('Gallery 2', 'gallery2');
var image1 = new GalleryImage('image1.jpg', 'img1');
var image2 = new GalleryImage('image2.jpg', 'img2');
var image3 = new GalleryImage('image3.jpg', 'img3');
var image4 = new GalleryImage('image4.jpg', 'img4');
gallery1.add(image1);
gallery1.add(image2);
gallery2.add(image3);
gallery2.add(image4);
container.add(gallery1);
container.add(gallery2);
// Make sure to add the top container to the body,
// otherwise it'll never show up.
container.getElement().appendTo('body');
container.show();
組合模式之利:
簡單的操作也能產(chǎn)生復(fù)雜的結(jié)果,只需對最頂層的對象執(zhí)行操作,讓每一個子對象自己傳遞這個操作即可。這對于那些再三執(zhí)行的操作尤其有用。
在組合模式中,各個對象之間的耦合非常松散。只要它們實現(xiàn)了同樣的接口那么改變它們的位置或互換它們只是舉手之勞。著促進了代碼的重用,也有利于代碼重構(gòu)。
每當(dāng)對頂層組合對象執(zhí)行一個操作時,實際上是在對整個結(jié)構(gòu)進行深度優(yōu)先的搜索以查找節(jié)點,而創(chuàng)建組合對象的程序員對這些細(xì)節(jié)一無所知。在這個層次體系中添加、刪除和查找節(jié)點都非常容易。
組合模式之弊:
組合對象的易用性可能掩蓋了它所支持的每一種操作的代價。由于組合對象調(diào)用的任何操作都會被傳遞到它的所有子對象如果這個層次體系很大的話,系統(tǒng)的性能將會受到影響。組合模式的正常運作需要用到某種形式的接口。
組合對象和節(jié)點類被用作HTML元素的包裝工具時,組合對象必須遵守HTML的使用規(guī)則。例如,表格就很難轉(zhuǎn)化為一個組合對象。
接口檢查越嚴(yán)格,組合對象類也就越可靠。