JavaScript設計模式--簡單工廠模式定義與應用案例詳解
本文例項講述了JavaScript設計模式--簡單工廠模式定義與應用。分享給大家供大家參考,具體如下:
一,介紹
工廠模式建立物件(視為工廠裡的產品)時無需指定建立物件的具體類。
工廠模式定義一個用於建立物件的介面,這個介面由子類決定例項化哪一個類。該模式使一個類的例項化延遲到了子類。而子類可以重寫介面方法以便建立的時候指定自己的物件型別。
在這裡將工廠簡單分為三種:
(1)簡單工廠:通過第三方的類完成鬆耦合的任務。
(2)複雜工廠:通過把例項化的任務交給子類來完成的,用以到達鬆耦合的目的。
(3)超級工廠:通過eval()來完成智慧工廠。
工廠的目的:在於判斷介面最終用哪個類例項化(故與介面密不可分)。
二,正文部分
工廠模式與介面是密不可分的所以我們需要先引入介面檔案和繼承類檔案
(1)介面檔案:
//定義一個靜態方法來實現介面與實現類的直接檢驗 //靜態方法不要寫出Interface.prototype,因為這是寫到介面的原型鏈上的 //我們要把靜態的函式直接寫到類層次上 //(1)定義一個介面類 var Interface=function (name,methods) {//name:介面名字 if(arguments.length<2){ alert("必須是兩個引數") } this.name=name; this.methods=[];//定義一個空陣列裝載函式名 for(var i=0;i<methods.length;i++){ if(typeof methods[i]!="string"){ alert("函式名必須是字串型別"); }else { this.methods.push( methods[i]); } } }; Interface.ensureImplement=function (object) { if(arguments.length<2){ throw new Error("引數必須不少於2個") return false; } for(var i=1;i<arguments.length;i++){ var inter=arguments[i]; //如果是介面就必須是Interface型別 if(inter.constructor!=Interface){ throw new Error("如果是介面類的話,就必須是Interface型別"); } //判斷介面中的方法是否全部實現 //遍歷函式集合 for(var j=0;j<inter.methods.length;j++){ var method=inter.methods[j];//介面中所有函式 //object[method]傳入的函式 //最終是判斷傳入的函式是否與介面中所用函式匹配 if(!object[method]||typeof object[method]!="function" ){//實現類中必須有方法名字與介面中所用方法名相同 throw new Error("實現類中沒有完全實現介面中的所有方法") } } } }
(2)繼承檔案
/*建立extend函式為了程式中所有的繼承操作*/ //subClass:子類 superClass:超類 function extend(subClass,superClass) { //1,使子類原型屬性等於父類的原型屬性 //初始化一箇中間空物件,目的是為了轉換主父關係 var F = function () {}; F.prototype = superClass.prototype; //2, 讓子類繼承F subClass.prototype = new F(); subClass.prototype.constructor = subClass; //3,為子類增加屬性 superClass ==》原型鏈的引用 subClass.superClass = superClass.prototype; //4,增加一個保險,就算你的原型類是超類(Object)那麼也要把你的建構函式級別降下來 if (superClass.prototype.constructor == Object.prototype.constructor) { superClass.prototype.constructor = superClass; } }
通過下面的例子,逐步引進工廠模式及改進工廠模式
1,工廠模式的引入,
(1)建立介面物件
var Pet=new Interface("Pet",["eat","run","sing","register"]);
(2)定義一個寵物店類並在prototype上進行擴充套件
var PetShop=function () {} PetShop.prototype={ //出售寵物的方法 sellPet:function (kind) { //寵物物件 var pet; //寵物種類 switch (kind){ case 'dog': pet=new Dog(); break; case 'cat': pet=new Cat(); break; case 'pig': pet=new Pig(); break; default: pet=new Bird(); } //驗證介面 Interface.ensureImplement(pet,Pet);//判斷pet物件是否全部實現介面Pet裡面全部的方法 (物件,介面) pet.eat(); pet.register(); return pet; } }
(3)分析寵物的一些特點可以將一些公共的部分提取出來(這裡只是簡單的提取)
//基類 有共同的提出來 function basePet() { this.register=function () { document.write("寵物登記...<br>"); } this.eat=function () { document.write("寵物吃飯...<br>"); } }
(4)各個實現類 ---這裡是各種動物
function Dog() { Dog.superClass.constructor.call(this);//繼承父類 //實現介面部分 this.run=function () { document.write("小狗跑......<br>") } this.sing=function () { document.write("小狗唱歌......<br>") } } function Cat() { Cat.superClass.constructor.call(this);//繼承父類 //實現介面部分 this.run=function () { document.write("小貓跑......<br>") } this.sing=function () { document.write("小貓唱歌......<br>") } } function Pig() { Pig.superClass.constructor.call(this);//繼承父類 //實現介面部分 this.run=function () { document.write("小豬跑......<br>") } this.sing=function () { document.write("小豬唱歌......<br>") } } function Bird() { Bird.superClass.constructor.call(this);//繼承父類 //實現介面部分 this.run=function () { document.write("小鳥跑......<br>") } this.sing=function () { document.write("小鳥唱歌......<br>") } }
(5)各個實現類繼承基類
//繼承
extend(Dog,basePet); extend(Cat,basePet); extend(Pig,basePet); extend(Bird,basePet);
(6)建立寵物的開始賣寵物
var newPetShop=new PetShop(); var flowerPig=newPetShop.sellPet("pig"); flowerPig.run();
結果為:
總結一下,上述好像沒怎麼體現有關工廠之類的,我們應該注意到這麼一個問題就是:當需要增加一個新品種寵物時,我們需要修改 '寵物店類',耦合度較高。
為了解決這個問題我們使用簡單工廠模式來解決。
2,簡單工廠模式(針對上述的改進)
(1)介面檔案與繼承檔案的的引入 同上面
(2)靜態工廠
//使用工廠方式建立寵物物件 // 靜態工廠 var factoryPet={ //出售寵物的方法 getPet:function (kind) { //寵物物件 var pet; //寵物種類 switch (kind){ case 'dog': pet=new Dog(); break; case 'cat': pet=new Cat(); break; case 'pig': pet=new Pig(); break; default: pet=new Bird(); } //驗證介面 Interface.ensureImplement(pet,Pet);//判斷pet物件是否全部實行介面Pet裡面全部的方法 return pet; } }
(3)利用工廠建立寵物店物件
var factoryPetShop=function () {} factoryPetShop.prototype={ getPet:function (kind) { var pet=factoryPet.getPet(kind); pet.eat(); pet.register(); return pet; } }
(4)從寵物店購買寵物實現
var newPetShop=new factoryPetShop(); var flowerCat=newPetShop.getPet("cat"); flowerCat.sing();
(5)使用簡單工廠實現的全部程式碼(數字標號表示其思考的先後順序)
(function () { //(2)介面呼叫 var Pet=new Interface("Pet","register"]); //(3)基類 分析後有共同的提出來作為基類 function basePet() { this.register=function () { document.write("寵物登記。。。。<br>"); } this.eat=function () { document.write("寵物吃飯。。。。<br>"); } } //(4)實現類 繼承基類+介面實現 function Dog() { Dog.superClass.constructor.call(this);//繼承父類 //實現介面 this.run=function () { document.write("小狗跑......<br>") } this.sing=function () { document.write("小狗唱歌......<br>") } } function Cat() { Cat.superClass.constructor.call(this);//繼承父類 //實現介面 this.run=function () { document.write("小貓跑......<br>") } this.sing=function () { document.write("小貓唱歌......<br>") } } function Pig() { Pig.superClass.constructor.call(this);//繼承父類 //實現介面 this.run=function () { document.write("小豬跑......<br>") } this.sing=function () { document.write("小豬唱歌......<br>") } } function Bird() { Bird.superClass.constructor.call(this);//繼承父類 //實現介面 this.run=function () { document.write("小鳥跑......<br>") } this.sing=function () { document.write("小鳥唱歌......<br>") } } //繼承 extend(Dog,basePet); extend(Cat,basePet); extend(Pig,basePet); extend(Bird,basePet); //(1)使用工廠方式建立寵物物件 // 靜態工廠 var factoryPet={ //出售寵物的方法 getPet:function (kind) { //寵物物件 var pet; //寵物種類 switch (kind){ case 'dog': pet=new Dog(); break; case 'cat': pet=new Cat(); break; case 'pig': pet=new Pig(); break; default: pet=new Bird(); } //驗證介面 Interface.ensureImplement(pet,Pet);//判斷pet物件是否全部實行介面Pet裡面全部的方法 return pet; } } //(5)利用工廠的寵物店物件(寵物店買寵物) var factoryPetShop=function () {} factoryPetShop.prototype={ getPet:function (kind) { var pet=factoryPet.getPet(kind); pet.eat(); pet.register(); return pet; } } //(6)從寵物店購買寵物 var newPetShop=new factoryPetShop();//寵物工廠 var flowerCat=newPetShop.getPet("cat");//從寵物工廠中得到寵物 flowerCat.sing(); })()
總結一下,上述看似完美,但是任有問題存在:比如說:張三的寵物店想賣哈士奇,李四的寵物店想賣鳥時,這樣的話,寵物都是通過一個工廠生產的,並不一定滿足各個賣家的需求。
所以我們需要根據各個廠家的需求,有不同的工廠,各個賣家可以根據自己需求使用不同的工廠(其實是利用不同子類實現各自合適的工廠),用於滿足每個寵物店的不同。
於是我們有了複雜的工廠用來解決該問題。
3,複雜工廠:通過把例項化的任務交給子類來完成的,用以到達鬆耦合的目的。
此處同樣是根據上述進行改進的,還是簡單的說明一下實現過程
(1)在html中將介面檔案的引進,程式碼為
//定義一個靜態方法來實現介面與實現類的直接檢驗 //靜態方法不要寫出Interface.prototype,因為這是寫到介面的原型鏈上的 //我們要把靜態的函式直接寫到類層次上 //定義一個介面類 var Interface=function (name,methods) {//name:介面名字 if(arguments.length<2){ alert("必須是兩個引數") } this.name=name; this.methods=[];//定義一個空陣列裝載函式名 for(var i=0;i<methods.length;i++){ if(typeof methods[i]!="string"){ alert("函式名必須是字串型別"); }else { this.methods.push( methods[i]); } } }; Interface.ensureImplement=function (object) { if(arguments.length<2){ throw new Error("引數必須不少於2個") return false; } for(var i=1;i<arguments.length;i++){ var inter=arguments[i]; //如果是介面就必須是Interface型別 if(inter.constructor!=Interface){ throw new Error("如果是介面類的話,就必須是Interface型別"); } //判斷介面中的方法是否全部實現 //遍歷函式集合 for(var j=0;j<inter.methods.length;j++){ var method=inter.methods[j];//介面中所有函式 //object[method]傳入的函式 //最終是判斷傳入的函式是否與介面中所用函式匹配 if(!object[method]||typeof object[method]!="function" ){//實現類中必須有方法名字與介面中所用方法名相同 throw new Error("實現類中沒有完全實現介面中的所有方法") } } } }
(2)在html中將繼承檔案引入,程式碼如下,
/*建立extend函式為了程式中所有的繼承操作*/ //subClass:子類 superClass:超類 function extend(subClass,superClass) { //1,使子類原型屬性等於父類的原型屬性 //初始化一箇中間空物件,目的是為了轉換主父關係 var F = function () {}; F.prototype = superClass.prototype; //2, 讓子類繼承F subClass.prototype = new F(); subClass.prototype.constructor = subClass; //3,為子類增加屬性 superClass ==》原型鏈的引用 subClass.superClass = superClass.prototype; //4,增加一個保險,就算你的原型類是超類(Object)那麼也要把你的建構函式級別降下來 if (superClass.prototype.constructor == Object.prototype.constructor) { superClass.prototype.constructor = superClass; } }
(3)分析各個類提出相同的部分作為基類,基類程式碼如下
//基類 分析後有共同的提出來作為基類 function basePet() { this.register=function () { document.write("寵物登記。。。。<br>"); }; this.eat=function () { document.write("寵物吃飯。。。。<br>"); } }
(4)各個具體的實現類:繼承基類+介面實現
//各個寵物類(實現類) 繼承基類+介面實現 function Dog() { Dog.superClass.constructor.call(this);//繼承父類 //實現介面 this.run=function () { document.write("小狗跑......<br>") } this.sing=function () { document.write("小狗唱歌......<br>") } } function Cat() { Cat.superClass.constructor.call(this);//繼承父類 //實現介面 this.run=function () { document.write("小貓跑......<br>") } this.sing=function () { document.write("小貓唱歌......<br>") } } function Pig() { Pig.superClass.constructor.call(this);//繼承父類 //實現介面 this.run=function () { document.write("小豬跑......<br>") } this.sing=function () { document.write("小豬唱歌......<br>") } } function Bird() { Bird.superClass.constructor.call(this);//繼承父類 //實現介面 this.run=function () { document.write("小鳥跑......<br>") }; this.sing=function () { document.write("小鳥唱歌......<br>") } }
(5)實現類與基類的繼承實現,程式碼如下(呼叫extend())
extend(Dog,basePet);//動物狗繼承基類 extend(Cat,basePet);
(6)將商店抽取出來,做成抽象類,程式碼如下
//把核心商店抽取出來 var petShop=function () {}; petShop.prototype={//模擬抽象類 需要被子類覆蓋 getPet:function (kind){ var pet=this.getpet(kind); pet.eat(); pet.register(); return pet; },getpet:function (model){ throw new Error("該類是抽象類,不能例項化") } };
(7)利用子類來滿足各個商家的不同型別寵物店的實現 ,程式碼如下
//利用子類來滿足之前的需求(多型) var oneShop=function () { } extend(oneShop,petShop);//繼承 //覆寫方法 oneShop.prototype.getpet=function (model) { //寵物物件 var pet; //寵物種類 switch (model){ case 'dog': pet=new Dog(); break; default: pet=new Bird(); } //驗證介面 Interface.ensureImplement(pet,Pet);//判斷pet物件是否全部實行介面Pet裡面全部的方法 pet.eat(); pet.register(); return pet; };
同上,這個也是一個不同的子類
twoShop=function () {}; extend(twoShop,petShop);//商店的繼承 //覆寫方法 twoShop.prototype.getPet=function (model) { //寵物物件 var pet; //寵物種類 switch (kind){ case 'pig': pet=new Pig(); break; default: pet=new Bird(); } //驗證介面 Interface.ensureImplement(pet,Pet);//判斷pet物件是否全部實行介面Pet裡面全部的方法 pet.eat(); pet.register(); return pet; };
(8) 使用,實質是子類對父類的例項化
這裡實現其中一個寵物店,另外一個同理。
//子類對父類的例項化 var jim=new oneShop(); var pig= jim.getpet("dog"); pig.run(); pig.sing()
(9)上述程式碼綜合在一起為,程式碼如下
(function () { //(2)介面呼叫 var Pet=new Interface("Pet","register"]); //(1)基類 分析後有共同的提出來作為基類 function basePet() { this.register=function () { document.write("寵物登記。。。。<br>"); }; this.eat=function () { document.write("寵物吃飯。。。。<br>"); } } //(3)實現類 繼承基類+介面實現 function Dog() { Dog.superClass.constructor.call(this);//繼承父類 //實現介面 this.run=function () { document.write("小狗跑......<br>") } this.sing=function () { document.write("小狗唱歌......<br>") } } function Cat() { Cat.superClass.constructor.call(this);//繼承父類 //實現介面 this.run=function () { document.write("小貓跑......<br>") } this.sing=function () { document.write("小貓唱歌......<br>") } } function Pig() { Pig.superClass.constructor.call(this);//繼承父類 //實現介面 this.run=function () { document.write("小豬跑......<br>") } this.sing=function () { document.write("小豬唱歌......<br>") } } function Bird() { Bird.superClass.constructor.call(this);//繼承父類 //實現介面 this.run=function () { document.write("小鳥跑......<br>") }; this.sing=function () { document.write("小鳥唱歌......<br>") } } //繼承 extend(Dog,basePet);//寵物的繼承 extend(Cat,basePet); //(4)把核心商店抽取出來 var petShop=function () {}; petShop.prototype={//模擬抽象類 需要被子類覆蓋 getPet:function (kind){ var pet=this.getpet(kind); pet.eat(); pet.register(); return pet; },getpet:function (model){ throw new Error("該類是抽象類,不能例項化") } }; //(5)商店1 利用子類來滿足之前的需求(多型) var oneShop=function () { } extend(oneShop,Pet);//判斷pet物件是否全部實行介面Pet裡面全部的方法 pet.eat(); pet.register(); return pet; }; //(5)商店2 twoShop=function () {}; extend(twoShop,Pet);//判斷pet物件是否全部實行介面Pet裡面全部的方法 pet.eat(); pet.register(); return pet; }; //(6)使用 子類對父類的例項化 var jim=new oneShop();//開寵物店 var pig= jim.getpet("dog");//從寵物店得到寵物 pig.run();//寵物功能 pig.sing() })();
注:程式碼中的註釋編號表示其大概思考過程及實現順序。
總結一下,在該個模式中主要體現在多型多一點。現在我們將前面的各種綜合在一起使用JavaScript的eval()做一個智慧化的工廠。
4,通過eval()實現智慧化工廠
(1)介面檔案和繼承檔案的引入,如上述的一模一樣,這裡將不再重複貼程式碼了,直接開始我們的新東西吧。
(2)介面呼叫
var Pet=new Interface("Pet","register"]);
(3)將相同部分提取出來(簡單的提取)
//基類 分析後有共同的提出來作為基類 function basePet() { this.register=function () { document.write("寵物登記。。。。<br>"); }; this.eat=function () { document.write("寵物吃飯。。。。<br>"); } }
(4)各動物類
//實現類 繼承基類+介面實現 function Dog() { Dog.superClass.constructor.call(this);//繼承父類 //實現介面 this.run=function () { document.write("小狗跑......<br>") } this.sing=function () { document.write("小狗唱歌......<br>") } } function Cat() { Cat.superClass.constructor.call(this);//繼承父類 //實現介面 this.run=function () { document.write("小貓跑......<br>") } this.sing=function () { document.write("小貓唱歌......<br>") } } function Pig() { Pig.superClass.constructor.call(this);//繼承父類 //實現介面 this.run=function () { document.write("小豬跑......<br>") } this.sing=function () { document.write("小豬唱歌......<br>") } } function Bird() { Bird.superClass.constructor.call(this);//繼承父類 //實現介面 this.run=function () { document.write("小鳥跑......<br>") }; this.sing=function () { document.write("小鳥唱歌......<br>") } }
(5)實現各動物類繼承基類
//繼承 extend(Dog,basePet);
(6)將商店核心抽取出來,做成一個抽象類,程式碼如下,
var petShop=function () {}; petShop.prototype={//模擬抽象類 需要被子類覆蓋 getPet:function (kind){ var pet=this.getpet(kind); pet.eat(); pet.register(); return pet; },getpet:function (model){ throw new Error("該類是抽象類,不能例項化") } };
//這裡是做成抽象類其中的getpet方法是通過子類實現的。
(7)做一個智慧工廠
//(5)智慧工廠 只負責生成寵物 var PetFactory={ sellPet:function (kind) { var pet; pet=eval("new "+kind+"()"); Interface.ensureImplement(pet,Pet);//判斷pet物件是否全部實行介面Pet裡面全部的方法 return pet; } }
(8)利用子類來滿足各個商家的不同型別寵物店的實現 ,程式碼如下
其中一個子類
//利用子類來滿足各個商家的不同型別寵物店的實現 (多型) var oneShop=function () { }; extend(oneShop,petShop);//繼承 //覆寫方法 oneShop.prototype.getpet=function (model) { //寵物物件 var pet=null; //寵物種類 var pets=["Dog","Cat","Bird"];//商店自己擁有的寵物 寵物貨架 for(v in pets){//迴圈出索引 if(pets[v]==model){//model是我們自己傳遞過來需要建立的寵物 pet=PetFactory.sellPet(model); //驗證介面 Interface.ensureImplement(pet,Pet);//判斷pet物件是否全部實行介面Pet裡面全部的方法 pet.eat(); pet.register(); break; } } return pet;
另一個子類
//(商店2)利用子類來滿足各個商家的不同型別寵物店的實現 (多型) twoShop=function () {}; extend(twoShop,petShop);//商店的繼承 //覆寫方法 twoShop.prototype.getPet=function (model) { //寵物物件 var pet=null; //寵物種類 var pets=["Pig"];//商店自己擁有的寵物 for(v in pets){//迴圈出索引 if(pets[v]==model){ pet=PetFactory.sellPet(model); //驗證介面 Interface.ensureImplement(pet,Pet);//判斷pet物件是否全部實行介面Pet裡面全部的方法 pet.eat(); pet.register(); break; } } return pet; };
(9)實現開寵物店賣寵物
這裡我們來開第二個商店,賣Pig
var shop=new twoShop();//建立商店 var pet=shop.getPet("Pig");//從商店中得到寵物 pet.run();//寵物的功能
(10)智慧化工廠的程式碼
(function () { //(1)介面呼叫 var Pet=new Interface("Pet","register"]); //(2)基類 分析後有共同的提出來作為基類 function basePet() { this.register=function () { document.write("寵物登記。。。。<br>"); }; this.eat=function () { document.write("寵物吃飯。。。。<br>"); } } //(3)各個動物類(實現類) 繼承基類+介面實現 function Dog() { Dog.superClass.constructor.call(this);//繼承父類 //實現介面 this.run=function () { document.write("小狗跑......<br>") } this.sing=function () { document.write("小狗唱歌......<br>") } } function Cat() { Cat.superClass.constructor.call(this);//繼承父類 //實現介面 this.run=function () { document.write("小貓跑......<br>") } this.sing=function () { document.write("小貓唱歌......<br>") } } function Pig() { Pig.superClass.constructor.call(this);//繼承父類 //實現介面 this.run=function () { document.write("小豬跑......<br>") } this.sing=function () { document.write("小豬唱歌......<br>") } } function Bird() { Bird.superClass.constructor.call(this);//繼承父類 //實現介面 this.run=function () { document.write("小鳥跑......<br>") }; this.sing=function () { document.write("小鳥唱歌......<br>") } } //繼承 extend(Dog,basePet); //(4)把核心商店抽取出來 var petShop=function () {}; petShop.prototype={//模擬抽象類 需要被子類覆蓋 getPet:function (kind){ var pet=this.getpet(kind); pet.eat(); pet.register(); return pet; },getpet:function (model){ throw new Error("該類是抽象類,不能例項化") } }; //(5)智慧工廠 只負責生成寵物 var PetFactory={ sellPet:function (kind) { var pet; pet=eval("new "+kind+"()"); Interface.ensureImplement(pet,Pet);//判斷pet物件是否全部實行介面Pet裡面全部的方法 return pet; } } //(6)(商店1)利用子類來滿足各個商家的不同型別寵物店的實現 (多型) var oneShop=function () { }; extend(oneShop,Pet);//判斷pet物件是否全部實行介面Pet裡面全部的方法 pet.eat(); pet.register(); break; } } return pet; }; //(商店2)利用子類來滿足各個商家的不同型別寵物店的實現 (多型) twoShop=function () {}; extend(twoShop,Pet);//判斷pet物件是否全部實行介面Pet裡面全部的方法 pet.eat(); pet.register(); break; } } return pet; }; //(7)開寵物店賣寵物 var shop=new twoShop(); var pet=shop.getPet("Pig"); pet.run(); })();
總結一下,該種智慧化工廠的特點體現在我們需要什麼寵物店時,我們可以直接通過智慧化工廠建立。很完美。
3,工廠模式的使用場景
1.需要根據不同引數產生不同例項,這些例項有一些共性的場景
2.使用者只需要使用產品,不需要知道產品的建立細節
注意:除非是適用場景,否則不可濫用工廠模式,會造成程式碼的複雜度。
4.簡單工廠模式優點
1.工廠類集中了所有物件的建立,便於物件建立的統一管理
2.物件的使用者僅僅是使用產品,實現了單一職責
3.便於擴充套件,如果新增了一種業務,只需要增加相關的業務物件類和工廠類中的生產業務物件的方法,不需要修改其他的地方。
感興趣的朋友可以使用線上HTML/CSS/JavaScript程式碼執行工具:http://tools.jb51.net/code/HtmlJsRun測試上述程式碼執行效果。
更多關於JavaScript相關內容感興趣的讀者可檢視本站專題:《javascript面向物件入門教程》、《JavaScript錯誤與除錯技巧總結》、《JavaScript資料結構與演算法技巧總結》、《JavaScript遍歷演算法與技巧總結》及《JavaScript數學運算用法總結》
希望本文所述對大家JavaScript程式設計有所幫助。