組合模式
- 什麼是組合模式
- 生活中的組合模式
- 組合模式的實際運用
- 為什麼使用js繼承
官方:
組合模式,將物件組合成樹形結構以表示“部分-整體”的層次結構,組合模式使得使用者對單個物件和組合物件的使用具有一致性。掌握組合模式的重點是要理解清楚 “部分/整體” 還有 ”單個物件“ 與 "組合物件" 的含義。
- 好了,你可以忽略我上面說的廢話,下面聽我BB。
傳說中的23
中設計模式的命名者已經告訴大家這大概是一個什麼樣的套路,但我們以後要討論的不僅僅要了解,還可以找機會使用。
生活中的組合模式
生活中還是有很多的組合模式的:麥當勞套餐
,飯店美團X人套餐
,聯通流量包
等等,他們把多個‘個體’組合成了一個‘整體’,這是生活中的例子,讓我用最近的實際小專案來說話。
舉個例子:
最近在公司有一個小模組叫‘甘特圖’,github
基本都是這樣的:
但是公司怎能要這種傳統的東西呢,必須要創新!!!具體有啥呢,傳統只有一條線
,老闆:給我加兩條
,一條預期
,一條實際
,要有mileStone
(里程碑),加個總進度
,加個....云云,信心滿滿的你是否被嚇到了呢,我個人實現了一份,不過可能使用組合模式會更好,我們下面簡單嘗試一下使用組合模式
吧 ?。
首先,我們確定我們做一個元件名字就叫Gantt
(肯德基)吧,元件包括什麼呢:分為這幾個小元件(套餐):畫圖
套餐(包括:進度條,背景網格,內容補充),格式化
套餐,加上一些簡單的工具類:獲得最長時間,日期格式化,只是給大家舉個例子。
做好之後大概這個樣子 :
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'元素逐漸逐漸向裡面畫的,裡面還有更加複雜的內容(計算最大時間範圍,畫背景,新增日曆等等)
下面還應該有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,還有一些亮點沒有提交,當時寫的比較匆忙,現在最近想想也許當時考慮用組合模式更加適合於以後需求的變更。極大節約開發時間。
### 最後希望大家能寫出更加風騷的程式碼
複製程式碼