一、設(shè)計模式
javascript里面給我們提供了很多種設(shè)計模式:
工廠、橋、組合、門面、適配器、裝飾者、享元、代理、觀察者、命令、責(zé)任鏈
在前面我們實(shí)現(xiàn)了工廠模式和橋模式
工廠模式 :
核心:為了生產(chǎn)對象,實(shí)現(xiàn)解耦。
橋接模式 :
(橋接模式是一種既能把兩個對象連接在一起,又能避免二者間的強(qiáng)耦合的方法。通過“橋”把彼此聯(lián)系起來,同時又允許他們各自獨(dú)立變化)
主要作用:主要作用表現(xiàn)為將抽象與其實(shí)現(xiàn)隔離開來,以便二者獨(dú)立化。
組合模式 :
(組合模式是一種專門為創(chuàng)建Web上的動態(tài)用戶界面而量身制定的模式。使用這種模式可以用一條命令在多個對象上激發(fā)復(fù)雜的或遞歸的行為。這可以簡化粘合性代碼,使其更容易維護(hù),而那些復(fù)雜行為則被委托給各個對象。)
優(yōu)點(diǎn) :
1 你可以用同樣的方法處理對象的集合與其中的特定子對象。
2 它可以用來把一批子對象組織成樹形結(jié)構(gòu),并且使整棵樹都可以被遍歷。
場景 :
1 存在一批組織成某種層次體系的對象
2 希望對這批對象或其中的一部分對象實(shí)施一個操作。
特點(diǎn) :
1 組合模式中只有兩種類型對象:組合對象、葉子對象
2 這兩種類型都實(shí)現(xiàn)同一批接口
3 一般我們會在組合對象中調(diào)用其方法并隱式調(diào)用"下級對象"的方法(這里我們一般采用遞歸的形式去做)
后面的模式后面具體用到在細(xì)說
看實(shí)例:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>組合設(shè)計模式</title>
<!--
@theme: javascript高級 組合模式設(shè)計
@autor:EthanCoco
@date:2015-11-22
@email:lijianlin0204@163.com
-->
<script type=text/javascript charset=utf-8>
//創(chuàng)建一個命名空間
var LJL = {};
/**
*建一個接口
*接口需要兩個參數(shù)
*參數(shù)1: 接口的名字(string)
*參數(shù)2: 方法名稱(數(shù)組(string))
*/
LJL.Interface = function(name,methods){
if(arguments.length !== 2){//判斷接口參數(shù)個數(shù)是否正確
throw new Error("參數(shù)個數(shù)不正確!");
}
this.name = name;
this.methods = [];//定義一個內(nèi)置的空數(shù)組接受方法名稱里的元素
for(var i = 0;i < methods.length;i++){
if(typeof methods[i] !== 'string'){
throw new Error("方法名稱錯誤,必須是字符串類型!");
}
//把傳入?yún)?shù)的元素全部放到內(nèi)置的methods中去
this.methods.push(methods[i]);
}
};
/**
*接口靜態(tài)方法
*參數(shù):object
*檢驗(yàn)接口里的方法是否實(shí)現(xiàn)
*如果通過不做任何操作,如果不通過,拋出error
*目的:檢測方法是否全部實(shí)現(xiàn)
*object 要求參數(shù)必須有兩個以上
*一個是對象實(shí)例
*其它是要檢驗(yàn)的接口對象
*/
LJL.Interface.checkMethodsIsPass = function(object){
if(arguments.length < 2){//判斷參數(shù),如果參數(shù)小于2,拋出異常
throw new Error("要求參數(shù)必須有兩個以上@param1=實(shí)例對象,其它參數(shù)是接口對象!");
}
//獲得接口實(shí)例對象
for(var i = 1;i<arguments.length;i++){//i=1是因?yàn)榈诙€參數(shù)是需要檢測的接口
var instanceInterface = arguments[i];
//判斷參數(shù)是否是接口對象
if(instanceInterface.constructor !== LJL.Interface){
throw new Error("參數(shù)是不是接口對象!");
}
//如果是,檢測接口對象里的方法是否實(shí)現(xiàn)
for(var j = 0;j<instanceInterface.methods.length;j++){
//用歷史變量接受一個方法的名稱,名稱是字符串,如果不是就拋出error
var methodName = instanceInterface.methods[j];
//object[key]表示的就是方法
//方法是一個函數(shù),需要判斷
if(!object[methodName] || typeof object[methodName] !== 'function'){
throw new Error("這個方法 '" + methodName + "' 找不到!");
}
}
}
};
/**
* 繼承方法
* @param {Object} sub
* @param {Object} sup
*/
/*
LJL.extend=function(sub ,sup){
// 目的: 實(shí)現(xiàn)只繼承父類的原型對象
var F = new Function(); // 1 創(chuàng)建一個空函數(shù) 目的:空函數(shù)進(jìn)行中轉(zhuǎn)
F.prototype = sup.prototype; // 2 實(shí)現(xiàn)空函數(shù)的原型對象和超類的原型對象轉(zhuǎn)換
sub.prototype = new F(); // 3 原型繼承
sub.prototype.constructor = sub ; // 4還原子類的構(gòu)造器
//保存一下父類的原型對象: 一方面方便解耦 另一方面方便獲得父類的原型對象
sub.superClass = sup.prototype; //自定義一個子類的靜態(tài)屬性 接受父類的原型對象
//判斷父類的原型對象的構(gòu)造器 (加保險)
if(sup.prototype.constructor == Object.prototype.constructor){
sup.prototype.constructor = sup ; //手動歡迎父類原型對象的構(gòu)造器
}
};
*/
/**
* 擴(kuò)展Array的原型對象 添加變量數(shù)組的每一個元素,并讓每一個元素都執(zhí)行fn函數(shù) (可變量多維數(shù)組)
* @param {Object} fn
*/
Array.prototype.each = function(fn){
try{
//1 目的: 遍歷數(shù)組的每一項(xiàng) //計數(shù)器 記錄當(dāng)前遍歷的元素位置
this.i || (this.i=0); //var i = 0 ;
//2 嚴(yán)謹(jǐn)?shù)呐袛嗍裁磿r候去走each核心方法
// 當(dāng)數(shù)組的長度大于0的時候 && 傳遞的參數(shù)必須為函數(shù)
if(this.length >0 && fn.constructor == Function){
// 循環(huán)遍歷數(shù)組的每一項(xiàng)
while(this.i < this.length){ //while循環(huán)的范圍
//獲取數(shù)組的每一項(xiàng)
var e = this[this.i];
//如果當(dāng)前元素獲取到了 并且當(dāng)前元素是一個數(shù)組
if(e && e.constructor == Array){
// 直接做遞歸操作
e.each(fn);
} else {
//如果不是數(shù)組 (那就是一個單個元素)
// 這的目的就是為了把數(shù)組的當(dāng)前元素傳遞給fn函數(shù) 并讓函數(shù)執(zhí)行
//fn.apply(e,[e]);
fn.call(e,e);
}
this.i++ ;
}
this.i = null ; // 釋放內(nèi)存 垃圾回收機(jī)制回收變量
}
} catch(ex){
// do something
}
return this ;
}
/********************************************************/
////////////////////////////////////////////////
//以上都是為設(shè)計模式做必要的準(zhǔn)備
////////////////////////////////////////////////
//開始組合設(shè)計模式
/**
* 組合模式應(yīng)用的場景和特點(diǎn):
* 場景:
* 1 存在一批組織成某種層次體系的對象
* 2 希望對這批對象或其中的一部分對象實(shí)施一個操作
*
* 應(yīng)用特點(diǎn):
* 1 組合模式中只有兩種類型對象:組合對象、葉子對象
* 2 這兩種類型都實(shí)現(xiàn)同一批接口
* 3 一般我們會在組合對象中調(diào)用其方法并隱式調(diào)用"下級對象"的方法(這里我們一般采用遞歸的形式去做)
*
*/
/*
* 場景模擬:
* -> 公司
* -> 北京分公司
* -> 財務(wù)部門
* -> 張1
* -> 張2
* -> 張3
* -> 銷售部門
* -> 張4
* -> 張5
* -> 張6
-> 長沙分公司
* -> 財務(wù)部門
* -> 張7
* -> 張8
* -> 張9
* -> 銷售部門
* -> 張10
* -> 張11
* -> 張12
*
* 實(shí)際的任務(wù)具體是落實(shí)到人上去實(shí)施的 也就是說只有人才具有具體的方法實(shí)現(xiàn)
*/
//創(chuàng)建組合對象的接口實(shí)例
var CompositeInterface = new LJL.Interface('CompositeInterface' , ['addChild','getChild']);
//創(chuàng)建葉子對象的接口實(shí)例
var LeafInterface = new LJL.Interface('LeafInterface' , ['hardworking','sleeping']);
/********************************************************/
/***********************組合對象*********************************/
//首先 : 組合模式中只有兩種類型對象:組合對象、葉子對象
//創(chuàng)建組合對象
var Composite = function(name){
this.name = name;
this.type = 'Composite'; //說明對象的類型(組合對象)
this.children = [] ; //承裝孩子的數(shù)組
//然后 :這兩種類型都實(shí)現(xiàn)同一批接口
//創(chuàng)建對象的最后要驗(yàn)證接口
LJL.Interface.checkMethodsIsPass(this,CompositeInterface,LeafInterface);
};
//在原型對象上實(shí)現(xiàn)接口方法
Composite.prototype = {
constructor:Composite , //還原構(gòu)造器
//實(shí)現(xiàn)CompositeInterface接口的addChildh和getChild方法
addChild:function(child){
//添加子節(jié)點(diǎn)到children上
this.children.push(child);
return this;//返回控制權(quán),實(shí)現(xiàn)鏈?zhǔn)讲僮? },
getChild:function(name){
//定義一個數(shù)組接受葉子對象類型
var elements = [] ;
//判斷對象是否是葉子對象類型,如果是添加到數(shù)組中去
//如果不是,則運(yùn)用遞歸繼續(xù)調(diào)用
var pushLeaf = function(item){
if(item.type === 'Composite'){
item.children.each(arguments.callee);
} else if(item.type === 'Leaf'){
elements.push(item);
}
};
// 根據(jù)name 讓指定name下的所有的類型為Leaf的對象去執(zhí)行操作
if(name && this.name !== name){
this.children.each(function(item){
// 如果傳遞的name是2級節(jié)點(diǎn)名稱
if(item.name === name && item.type === 'Composite'){
item.children.each(pushLeaf);
}
// 如果傳遞的name是3級節(jié)、4級、5級...N級
if(item.name !== name && item.type === 'Composite'){
item.children.each(arguments.callee);
}
// 如果傳遞的name是葉子節(jié)點(diǎn)的時候
if(item.name === name && item.type === 'Leaf'){
elements.push(item);
}
});
}else{ // 不傳遞name 讓整個公司所有類型為Leaf的對象去執(zhí)行操作
this.children.each(pushLeaf);
}
return elements ;
},
//實(shí)現(xiàn)LeafInterface接口的hardworking和sleeping方法
hardworking:function(name){
//得到所有的Leaf類型的對象數(shù)組
var leafObjects = this.getChild(name);
for(var i = 0 ; i < leafObjects.length; i ++){
leafObjects[i].hardworking();
}
},
sleeping:function(name){
//得到所有的Leaf類型的對象數(shù)組
var leafObjects = this.getChild(name);
for(var i = 0 ; i < leafObjects.length; i ++){
leafObjects[i].sleeping();
}
}
};
/***********************組合對象*********************************/
/***********************葉子對象*********************************/
//同樣在葉子原型對象上實(shí)現(xiàn)接口方法
var Leaf = function(name){
this.name = name;
this.type = 'Leaf'; //說明對象的類型(葉子對象)
//創(chuàng)建對象的最后要驗(yàn)證接口
LJL.Interface.checkMethodsIsPass(this,CompositeInterface,LeafInterface);
};
Leaf.prototype = {
constructor:Leaf ,//還原構(gòu)造器
//實(shí)現(xiàn)CompositeInterface接口的addChildh和getChild方法
addChild:function(child){
//讓其不能使用這個方法
throw new Error('this method is disabled....');
},
getChild:function(name){
if(this.name = name){
return this ;
}
return null ;
},
//實(shí)現(xiàn)LeafInterface接口的hardworking和sleeping方法
hardworking:function(){
document.write(this.name + '...努力工作!');
},
sleeping:function(){
document.write(this.name + '...努力睡覺!');
}
};
/***********************葉子對象*********************************/
/***********************測試單元*********************************/
//測試數(shù)據(jù)
//創(chuàng)建人的葉子對象
var p1 = new Leaf('張1');
var p2 = new Leaf('張2');
var p3 = new Leaf('張3');
var p4 = new Leaf('張4');
var p5 = new Leaf('張5');
var p6 = new Leaf('張6');
var p7 = new Leaf('張7');
var p8 = new Leaf('張8');
var p9 = new Leaf('張9');
var p10 = new Leaf('張10');
var p11 = new Leaf('張11');
var p12 = new Leaf('張12');
//創(chuàng)建公司部門
var dept1 = new Composite('北京開發(fā)部門');
//把p1,p2,p3三個人指定到dept1中去
dept1.addChild(p1).addChild(p2).addChild(p3);
var dept2 = new Composite('北京銷售部門');
dept2.addChild(p4).addChild(p5).addChild(p6);
var dept3 = new Composite('長沙開發(fā)部門');
dept3.addChild(p7).addChild(p8).addChild(p9);
var dept4 = new Composite('長沙銷售部門');
dept4.addChild(p10).addChild(p11).addChild(p12);
//創(chuàng)建組織分公司
var org1 = new Composite('北京分公司');
//把dept1和dept2指定到org1中去
org1.addChild(dept1).addChild(dept2);
var org2 = new Composite('長沙分公司');
org2.addChild(dept3).addChild(dept4);
//創(chuàng)建總部
var org = new Composite('尚學(xué)堂總部');
//把分公司掛到總部
org.addChild(org1).addChild(org2);
// 讓整個公司下所有的員工都去努力工作
org.hardworking(); //尚學(xué)堂總部
document.write('<Br>----------------------------------<Br>');
// name為總公司的直接子節(jié)點(diǎn)的時候
org.hardworking('長沙分公司');
document.write('<Br>----------------------------------<Br>');
// name為總公司的間接子節(jié)點(diǎn)的時候(類型不為leaf)(3級4級...N級)
org.hardworking('長沙開發(fā)部門');
document.write('<Br>----------------------------------<Br>');
// name為leaf對象的時候
org.hardworking('張5');
document.write('<Br>----------------------------------<Br>');
/***********************測試單元*********************************/
</script>
</head>
<body>
</body>
</html>