在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ HTML/ 設計模式之組合模式
代碼復用模式(避免篇)
S.O.L.I.D 五大原則之接口隔離原則 ISP
設計模式之狀態(tài)模式
JavaScript 核心(晉級高手必讀篇)
設計模式之建造者模式
JavaScript 與 DOM(上)——也適用于新手
設計模式之中介者模式
設計模式之裝飾者模式
設計模式之模板方法
設計模式之外觀模式
強大的原型和原型鏈
設計模式之構(gòu)造函數(shù)模式
揭秘命名函數(shù)表達式
深入理解J avaScript 系列(結(jié)局篇)
執(zhí)行上下文(Execution Contexts)
函數(shù)(Functions)
《你真懂 JavaScript 嗎?》答案詳解
設計模式之適配器模式
設計模式之組合模式
設計模式之命令模式
S.O.L.I.D 五大原則之單一職責 SRP
編寫高質(zhì)量 JavaScript 代碼的基本要點
求值策略
閉包(Closures)
對象創(chuàng)建模式(上篇)
This? Yes,this!
設計模式之代理模式
變量對象(Variable Object)
S.O.L.I.D 五大原則之里氏替換原則 LSP
面向?qū)ο缶幊讨话憷碚?/span>
設計模式之單例模式
Function 模式(上篇)
S.O.L.I.D 五大原則之依賴倒置原則 DIP
設計模式之迭代器模式
立即調(diào)用的函數(shù)表達式
設計模式之享元模式
設計模式之原型模式
根本沒有“JSON 對象”這回事!
JavaScript 與 DOM(下)
面向?qū)ο缶幊讨?ECMAScript 實現(xiàn)
全面解析 Module 模式
對象創(chuàng)建模式(下篇)
設計模式之職責鏈模式
S.O.L.I.D 五大原則之開閉原則 OCP
設計模式之橋接模式
設計模式之策略模式
設計模式之觀察者模式
代碼復用模式(推薦篇)
作用域鏈(Scope Chain)
Function 模式(下篇)
設計模式之工廠模式

設計模式之組合模式

介紹

組合模式(Composite)將對象組合成樹形結(jié)構(gòu)以表示“部分-整體”的層次結(jié)構(gòu),組合模式使得用戶對單個對象和組合對象的使用具有一致性。

常見的場景有 asp.net 里的控件機制(即 control 里可以包含子 control,可以遞歸操作、添加、刪除子 control),類似的還有 DOM 的機制,一個 DOM 節(jié)點可以包含子節(jié)點,不管是父節(jié)點還是子節(jié)點都有添加、刪除、遍歷子節(jié)點的通用功能。所以說組合模式的關鍵是要有一個抽象類,它既可以表示子元素,又可以表示父元素。

正文

舉個例子,有家餐廳提供了各種各樣的菜品,每個餐桌都有一本菜單,菜單上列出了該餐廳所偶的菜品,有早餐糕點、午餐、晚餐等等,每個餐都有各種各樣的菜單項,假設不管是菜單項還是整個菜單都應該是可以打印的,而且可以添加子項,比如午餐可以添加新菜品,而菜單項咖啡也可以添加糖啊什么的。

這種情況,我們就可以利用組合的方式將這些內(nèi)容表示為層次結(jié)構(gòu)了。我們來逐一分解一下我們的實現(xiàn)步驟。

第一步,先實現(xiàn)我們的“抽象類”函數(shù) MenuComponent

var MenuComponent = function () {
};
MenuComponent.prototype.getName = function () {
    throw new Error("該方法必須重寫!");
};
MenuComponent.prototype.getDescription = function () {
    throw new Error("該方法必須重寫!");
};
MenuComponent.prototype.getPrice = function () {
    throw new Error("該方法必須重寫!");
};
MenuComponent.prototype.isVegetarian = function () {
    throw new Error("該方法必須重寫!");
};
MenuComponent.prototype.print = function () {
    throw new Error("該方法必須重寫!");
};
MenuComponent.prototype.add = function () {
    throw new Error("該方法必須重寫!");
};
MenuComponent.prototype.remove = function () {
    throw new Error("該方法必須重寫!");
};
MenuComponent.prototype.getChild = function () {
    throw new Error("該方法必須重寫!");
};

該函數(shù)提供了 2 種類型的方法,一種是獲取信息的,比如價格,名稱等,另外一種是通用操作方法,比如打印、添加、刪除、獲取子菜單。

第二步,創(chuàng)建基本的菜品項

var MenuItem = function (sName, sDescription, bVegetarian, nPrice) {
    MenuComponent.apply(this);
    this.sName = sName;
    this.sDescription = sDescription;
    this.bVegetarian = bVegetarian;
    this.nPrice = nPrice;
};
MenuItem.prototype = new MenuComponent();
MenuItem.prototype.getName = function () {
    return this.sName;
};
MenuItem.prototype.getDescription = function () {
    return this.sDescription;
};
MenuItem.prototype.getPrice = function () {
    return this.nPrice;
};
MenuItem.prototype.isVegetarian = function () {
    return this.bVegetarian;
};
MenuItem.prototype.print = function () {
    console.log(this.getName() + ": " + this.getDescription() + ", " + this.getPrice() + "euros");
};

由代碼可以看出,我們只重新了原型的 4 個獲取信息的方法和 print 方法,沒有重載其它3個操作方法,因為基本菜品不包含添加、刪除、獲取子菜品的方式。

第三步,創(chuàng)建菜品

var Menu = function (sName, sDescription) {
    MenuComponent.apply(this);
    this.aMenuComponents = [];
    this.sName = sName;
    this.sDescription = sDescription;
    this.createIterator = function () {
        throw new Error("This method must be overwritten!");
    };
};
Menu.prototype = new MenuComponent();
Menu.prototype.add = function (oMenuComponent) {
    // 添加子菜品
    this.aMenuComponents.push(oMenuComponent);
};
Menu.prototype.remove = function (oMenuComponent) {
    // 刪除子菜品
    var aMenuItems = [];
    var nMenuItem = 0;
    var nLenMenuItems = this.aMenuComponents.length;
    var oItem = null;
    for (; nMenuItem < nLenMenuItems; ) {
        oItem = this.aMenuComponents[nMenuItem];
        if (oItem !== oMenuComponent) {
            aMenuItems.push(oItem);
        }
        nMenuItem = nMenuItem + 1;
    }
    this.aMenuComponents = aMenuItems;
};
Menu.prototype.getChild = function (nIndex) {
    //獲取指定的子菜品
    return this.aMenuComponents[nIndex];
};
Menu.prototype.getName = function () {
    return this.sName;
};
Menu.prototype.getDescription = function () {
    return this.sDescription;
};
Menu.prototype.print = function () {
    // 打印當前菜品以及所有的子菜品
    console.log(this.getName() + ": " + this.getDescription());
    console.log("--------------------------------------------");
    var nMenuComponent = 0;
    var nLenMenuComponents = this.aMenuComponents.length;
    var oMenuComponent = null;
    for (; nMenuComponent < nLenMenuComponents; ) {
        oMenuComponent = this.aMenuComponents[nMenuComponent];
        oMenuComponent.print();
        nMenuComponent = nMenuComponent + 1;
    }
};

注意上述代碼,除了實現(xiàn)了添加、刪除、獲取方法外,打印 print 方法是首先打印當前菜品信息,然后循環(huán)遍歷打印所有子菜品信息。

第四步,創(chuàng)建指定的菜品

我們可以創(chuàng)建幾個真實的菜品,比如晚餐、咖啡、糕點等等,其都是用 Menu 作為其原型,代碼如下:

var DinnerMenu = function () {
    Menu.apply(this);
};
DinnerMenu.prototype = new Menu();
var CafeMenu = function () {
    Menu.apply(this);
};
CafeMenu.prototype = new Menu();
var PancakeHouseMenu = function () {
    Menu.apply(this);
};
PancakeHouseMenu.prototype = new Menu();

第五步,創(chuàng)建最頂級的菜單容器——菜單本

var Mattress = function (aMenus) {
    this.aMenus = aMenus;
};
Mattress.prototype.printMenu = function () {
    this.aMenus.print();
};

該函數(shù)接收一個菜單數(shù)組作為參數(shù),并且值提供了 printMenu 方法用于打印所有的菜單內(nèi)容。

第六步,調(diào)用方式

var oPanCakeHouseMenu = new Menu("Pancake House Menu", "Breakfast");
var oDinnerMenu = new Menu("Dinner Menu", "Lunch");
var oCoffeeMenu = new Menu("Cafe Menu", "Dinner");
var oAllMenus = new Menu("ALL MENUS", "All menus combined");
oAllMenus.add(oPanCakeHouseMenu);
oAllMenus.add(oDinnerMenu);
oDinnerMenu.add(new MenuItem("Pasta", "Spaghetti with Marinara Sauce, and a slice of sourdough bread", true, 3.89));
oDinnerMenu.add(oCoffeeMenu);
oCoffeeMenu.add(new MenuItem("Express", "Coffee from machine", false, 0.99));
var oMattress = new Mattress(oAllMenus);
console.log("---------------------------------------------");
oMattress.printMenu();
console.log("---------------------------------------------");

熟悉 asp.net 控件開發(fā)的同學,是不是看起來很熟悉?

總結(jié)

組合模式的使用場景非常明確:

  1. 你想表示對象的部分-整體層次結(jié)構(gòu)時;
  2. 你希望用戶忽略組合對象和單個對象的不同,用戶將統(tǒng)一地使用組合結(jié)構(gòu)中的所有對象(方法)

另外該模式經(jīng)常和裝飾者一起使用,它們通常有一個公共的父類(也就是原型),因此裝飾必須支持具有 add、remove、getChild 操作的 component 接口。