JavaScript 設計模式 :用組合模式寫出複雜組件

組合模式

  • 什麼是組合模式
  • 生活中的組合模式
  • 組合模式的實際運用
  • 爲何使用js繼承

js繼承文獻javascript

官方: 組合模式,將對象組合成樹形結構以表示「部分-總體」的層次結構,組合模式使得用戶對單個對象和組合對象的使用具備一致性。掌握組合模式的重點是要理解清楚 「部分/總體」 還有 」單個對象「 與 "組合對象" 的含義。css

  • 好了,你能夠忽略我上面說的廢話,下面聽我BB。

傳說中的23中設計模式的命名者已經告訴你們這大概是一個什麼樣的套路,但咱們之後要討論的不只僅要了解,還能夠找機會使用。html

生活中的組合模式

生活中仍是有不少的組合模式的:麥當勞套餐飯店美團X人套餐聯通流量包等等,他們把多個‘個體’組合成了一個‘總體’,這是生活中的例子,讓我用最近的實際小項目來講話。java

舉個栗子:

最近在公司有一個小模塊叫‘甘特圖’,github基本都是這樣的: jquery

可是公司怎能要這種傳統的東西呢,必需要創新!!!具體有啥呢,傳統只有一條線,老闆:給我加兩條,一條預期,一條實際,要有mileStone(里程碑),加個總進度,加個....云云,信心滿滿的你是否被嚇到了呢,我我的實現了一份,不過可能使用組合模式會更好,咱們下面簡單嘗試一下使用組合模式吧 😆。git

首先,咱們肯定咱們作一個組件名字就叫Gantt(肯德基)吧,組件包括什麼呢:分爲這幾個小組件(套餐):畫圖套餐(包括:進度條,背景網格,內容補充),格式化套餐,加上一些簡單的工具類:得到最長時間,日期格式化,只是給你們舉個栗子。github

作好以後大概這個樣子 : 編程

github開源地址

這裏作一下規範,下文中,我將把Gantt叫作(商店)小組件叫作(套餐),組件內原型方法叫作(成員)。設計模式

  • 先造一個繼承器輪子↓
//創建一個屬於咱們本身的Jquery,裏面只有inheritObject、inheritPrototype兩個方法,
    (function () {
    var util = {
        inheritObject: function (o) {//對象繼承封裝
            var F = function () {
            };
            F.prototype = o;
            return new F();
        },
        inheritPrototype: function (subclass, supperclass) {//原型繼承封裝
            var obj = this.inheritObject(supperclass.prototype);
            obj.constructor = subclass;
            subclass.prototype = obj;
        }
    };
    window.$ = window.util = util;
})(window);//把閉包變量弄到全局

var Gantt = function (data) {
    this.ganttData = data;
    this.children = [];
    this.element = null;
}

Gantt.prototype = {
    init: function () {
        throw new Error('此方法必須子類重寫')
    },
    build: function () {
        throw new Error('此方法必須子類重寫')
    },

}

/**
 * 建立 Gantt外層容器
 * @param name
 * @param parent
 * @constructor
 */
var Container = function (name, parent) {
    Gantt.call(this);
    this.name = name;
    this.parent = parent;
    this.init();//構建子容器的基本點(id,dom,name)
}
$.inheritPrototype(Container, Gantt); //


Container.prototype = {
    /**
     *重寫父類init
     */
    init: function () {
        this.element = document.createElement('div');//建立一個div元素
        this.element.name = this.name;
        this.element.id = 'ganttView';
        this.parent.append(this.element);
    },
    /**
     *重寫父類build
     */
    build: function (child, text) {
        child.append(document.createTextNode(text)); //添加測試描述
        this.children.push(child);
        this.element.appendChild(child);
        return this;
    },
    getElement: function () {
        return this.element;
    },
    draw: function () {
        this.parent.appendChild(this.element)
    }
}
//調用方法
var ganttView = new Container('GanttView', document.body);
ganttView.build(document.createElement("div"), '左側詳情項目1')
    .build(document.createElement("div"), '左側詳情項目2').build(document.createElement("div"), '右側畫圖')
      
   
    ```
    
  
節約時間css就不寫了,
複製代碼
float:left;
height,width,
color,backbround,
text:center.....腦補中...
```
複製代碼

那麼咱們注意,Container是個次級容器,是根據一個parent也就是最原始的'body'元素逐漸逐漸向裏面畫的,裏面還有更加複雜的內容(計算最大時間範圍,畫背景,添加日曆等等)bash

下面還應該有item裏面增長描述詳情畫圖等 固然描述,詳情,畫圖都只是‘套餐’,仍是須要最底層的‘成員’來作地基的,成員是最基層的,他再也不擁有子類,可是他們繼承了父類。

那麼咱們總體修改一下代碼:

var Gantt = function (data) {
        this.ganttData = data;
        this.children = [];
        this.element = null;
    }

    Gantt.prototype = {
        init: function () {
            throw new Error('此方法必須子類重寫')
        },
        build: function () {
            throw new Error('此方法必須子類重寫')
        },

    }

    /**
     * 建立 Gantt外層容器
     * @param name
     * @param parent
     * @constructor
     */
    var Container = function (name, parent) {
        Gantt.call(this);
        this.name = name;
        this.parent = parent;
        this.init();//構建子容器的基本點(id,dom,name)
    }
    $.inheritPrototype(Container, Gantt); //


    Container.prototype = {
        /**
         *重寫父類init
         */
        init: function () {
            this.element = document.createElement('div');//建立一個div元素
            this.element.id = 'ganttView';
            this.element.textContent= this.name;
            this.parent.append(this.element);
        },
        /**
         *重寫父類build
         */
        build: function (child) {
//            child.append(document.createTextNode(text)); //添加測試描述
            this.children.push(child);
            this.element.appendChild(child.element);
            return this;
        },
        getElement: function () {
            return this.element;
        },
        draw: function () {
            this.parent.appendChild(this.element)
        }
    }

    /**
     * 建立 Gantt基礎成員
     * @param name
     * @param parent
     * @constructor
     */
    var Item = function (name, parent) {
        Gantt.call(this);
        this.name = name;
        this.parent = parent;
        this.init();//構建子容器的基本點(id,dom,name)
    }
    $.inheritPrototype(Item, Container); //

    Item.prototype = {
        /**
         *重寫父類init
         */
        init: function () {
            this.element = document.createElement('div');//建立一個div元素
            this.element.id = 'ganttItem';
            this.element.textContent = this.name;
//            this.parent.append(this.element);
//            return this.element;
        },
        /**
         *重寫父類build
         */
        build: function (text) {
            //能夠再畫進度條,以及爲進度條綁定點擊事件
        },
        getElement: function () {
            return this.element;
        },
        draw: function () {
            this.parent.appendChild(this.element)
        }
    }

    //調用方法
    var container = new Container('GanttView', document.body);
        container.build(new Item('左側詳情項目1'))
                .build(new Item('左側詳情項目2'))
                
                ```
 
 ![](https://user-gold-cdn.xitu.io/2017/10/18/997cfd2b701a539045c4dad52aa460f3)
    
- 爲何要使用`繼承`,賊麻煩.我直接`new`不行麼?

 「行,可是記住一點,咱們使用了組合模式就要保障接口的統一,這也是[面向對象編程](https://juejin.im/post/58ff6374570c350058f489b5)的思想,相似`java`的`interface`接口,這樣咱們處理回調、異常更加方便,簡化了複雜的總體,又經過子類豐富了總體。「
    
我原來寫的甘特圖,是一次性生成的,若是產品提出,若使用`長輪循`監聽到服務器,若是服務器增長了一個任務,以動畫的形式動態添加一條進度條,這對於我之前的組件來講改動比較大,可是這個就很簡單了,可能咱們加一個add原型方法繼承就行了。
    
- 那麼組合模式有啥好處?
    「使得項目更加模塊化,一個組件能夠無限向下分紅各個套餐,直到到達最底層的成員,以原子粒度書寫代碼,對於代碼維護十分有利。」
    
    
開源的[Gantt插件](https://github.com/pkwenda/gantt.js)是基於`Jquery`的擴展插件,沒有用到svg等笨重的組件,只依賴jquery,只有14k,還有一些亮點沒有提交,當時寫的比較匆忙,如今最近想一想也許當時考慮用組合模式更加適合於之後需求的變動。極大節約開發時間。
    
### 最後但願你們能寫出更加風騷的代碼
 
複製代碼
相關文章
相關標籤/搜索