Javascript亂彈設計模式系列(2) - 抽象工廠以及工廠方法模式(Factory)

 前言

博客園談設計模式的文章很多,我也受益匪淺,包括TerryLee呂震宇等等的.NET設計模式系列文章,強烈推薦。對於我,擅長於前臺代碼的開發,對於設計模式也有一定的瞭解,於是我想結合Javascript來設計前臺方面的“設計模式”,以對後臺“設計模式”做個補充。開始這個系列我也誠惶誠恐,怕自己寫得不好,不過我也想做個嘗試,一來希望能給一些人有些幫助吧,二來從寫文章中鍛鍊下自己,三來通過寫文章對自己增加自信;如果寫得不好,歡迎拍磚,我會虛心向博客園高手牛人們學習請教;如果覺得寫得還可以,謝謝大家的支持了:)

概述

抽象工廠模式廣泛應用於因爲需求變動導致一些相似對象的創建工作,拿我們做項目最熟悉的多版本數據庫,根據數據庫的不同,有可能有SQLServer,Access,Oracle,MySQL版本等等的數據庫,這樣我們就能運用工廠模式,把各個數據庫中相似的功能操作封裝到它們各自的對象模型中,通過工廠對象統一創建各個對象實例,是客戶程序和這些具體對象實現了鬆耦合;而工廠方法模式主要是針對某個對象的需求變更,但是這個對象不會隨着它的變動而導致它所在的業務流程中的變動,它具有高聚合的能力,因此它的外部接口是很穩定的;

定義

抽象工廠模式是每個抽象產品派生多個具體產品類,每個抽象工廠派生多個具體工廠類,每個具體工廠負責多個(一系列)具體產品的實例創建。

工廠方法模式是每個抽象產品派生多個具體產品類,每個抽象工廠類派生多個具體工廠類,每個具體工廠類負責一個具體產品的實例創建。

類圖

抽象工廠模式類圖

 

工廠方法模式類圖

 

 

實例分析

在開始工廠模式之前,有必要先介紹下簡單工廠的說法,爲了避免在客戶程序的對象出現"if...else..."代碼難以擴展以及維護,這裏創建一個工廠類來封裝這些對象,那這個就應用了簡單工廠的方式。

這個場景是這樣,有個博客網站,在網站中用戶分爲幾種類型,我這裏暫且暫且分爲遊客,博客會員,超級管理員三種用戶權限:
   遊客只能進行簡單的查看博客的文章,個人資料等等;
   博客會員還可以對自己的博客進行管理; 
   超級管理員還可以對於博客系統後臺進行管理。
在開始介紹之前,先貼出前兩篇介紹的一些接口和類繼承的JS文件InterfaceAndClass.js,這裏主要要說的是類繼承的寫法:

function inheritClass(subClass, superClass)
{    
    
var Func = function() {};
    
for(p in superClass.prototype)
    {
        Func.prototype[p] 
= superClass.prototype[p];
    }
    subClass.prototype 
= new Func();
    subClass.prototype.constructor 
= subClass;
}

這裏將父類superClass原型的所有方法賦值給新創建的函數類,之後把函數類的原型賦值於子類subClass原型中。

一、現在先對Javascript簡單工廠進行介紹

1. 添加用戶類型接口IMember.jsgetMemberInfo做爲IMember接口的接口方法。

var IMember = new Interface("IMember", [["getMemberInfo"]]);

 

2.  添加三個具體用戶類型的類Guest.js,BlogMember.js,SuperAdmin.js,繼承IMember接口:

//遊客類
function Guest() { 
    Interface.registerImplements(
this, IMember); //繼承IMember接口
}
Guest.prototype 
= {
    getMemberInfo : 
function() {
        
return "遊客";
    },
    getData : 
function() {
        
return "";
    }
}

//博客會員類
function BlogMember() {
    Interface.registerImplements(
this, IMember);
}
BlogMember.prototype 
= {
    getMemberInfo : 
function() {
        
return "博客會員";
    },
    getData : 
function() {
        
return "";
    }
}

//超級管理員類
function SuperAdmin() {
    Interface.registerImplements(
this, IMember);
}
SuperAdmin.prototype 
= {
    getMemberInfo : 
function() {
        
return "超級管理員";
    }, 
    getData : 
function() {
        
return "";
    }
}

3. 創建一個用戶類型工廠類來封裝這些用戶類型的操作,添加MemberFactory.js:

var MemberFactory = {
    createMemberType : 
function(memberType) {
        
var  _memberType;
        
switch(memberType)
        {
            
case "guest": _memberType = new Guest(); break;
            
case "blogmember": _memberType = new BlogMember(); break;
            
case "superadmin": _memberType = new SuperAdmin(); break;
            
default: _memberType = new BlogMember(); break;
        }
        
return _memberType;
    }
}

通過memberType的“枚舉”,創建相應的用戶類型類的對象;

4. 至此,通過MemberFactory.createMemberType的“靜態方法”返回一個用戶類型對象;

var member = MemberFactory.createMemberType("guest");  //guest, blogmember, superadmin
$("#result").html("您當前爲:" + member.getMemberInfo());

(這裏$(“…”)寫法是jquery類庫中的語法,詳細請看官方文檔http://jquery.com/

二、接下來開始本篇的重點,Javascript工廠模式的網站應用

1. 延續上面的博客網站的場景:

   遊客只能進行簡單的查看博客的文章,個人資料等等;
   博客會員還可以對自己的博客進行管理;
   超級管理員還可以對於博客系統後臺進行管理;

這裏有這些標籤Tab:首頁,文章,電影,音樂,相冊,關於我,文章管理,個人資料管理,系統後臺管理,其中 遊客只能訪問“首頁,文章,電影,音樂,相冊,關於我”,博客會員(登錄後)增加訪問“文章管理,個人資料管理”,超級管理員增加訪問“系統後臺管理”;另外博客會員和超級管理員擁有修改皮膚顏色和版塊類型的功能;

 最終頁面顯示如下所示:

從圖上可以看出,博客會員以上的用戶類型可以顯示“文章管理,個人資料管理”標籤,超級管理員可以顯示“系統後臺管理”;而佈局選擇包括“左,中,右結構”,“左,右上,右下結構”,“左上,左下,右結構”,顏色包括“藍”,“紅”,“綠”,“紫”,這裏只能博客會員和超級管理員纔可以顯示;
(這裏我使用了網上提供的jquery.tab.js插件對標籤進行實現,詳細請看http://stilbuero.de/jquery/tabs_3/

好了,現在開始介紹這個實例通過Javascript工廠模式是如何實現的。

2.  IMember.js不變,從簡單工廠中直接複製。

3. IMember的具體實現類Guest,BlogMember,SuperAdmin從簡單工廠中複製,這裏分別添加個原型方法isSetColorBlock,判斷該用戶類型是否可設置顏色和佈局,如下所示:

Guest.prototype = {
    
//
    isSetColorBlock : function() {
        
return false;
    }
}

BlogMember.prototype 
= {
    
//
    isSetColorBlock : function() {
        
return true;
    }
}

SuperAdmin.prototype 
= {
    
//
    isSetColorBlock : function() {
        
return true;
    }
}

可以看到遊客不能進行設置,而博客會員和超級管理能進行設置;

4. MemberFactory.js不變,從簡單工廠直接複製。

5. 添加IBlock.js,創建佈局接口:

var IBlock = new Interface("IBlock", [["getData"]]);

6. 實現它的具體類,這裏添加LMRBlock.js(左 中 右 佈局),LRMBlock.js(左上 左下 右 佈局),MLRBlock.js(左 右上 右下 佈局),這裏以LMRBlock.js爲例:

// 左、中、右結構樣式版塊
function LMRBlock()
{
    
this.color = "blue";
    Interface.registerImplements(
this, IBlock); //繼承佈局IBlock接口
}
LMRBlock.prototype 
= {
    displayBlock : 
function() {
        
this.getData();
        
// 具體佈局實現
    },
    getData : 
function() {
        
return new Error("抽象方法,不能調用");
    }
}

這裏首先創建的是類似於一個抽象類,該類首先繼承於佈局接口,從代碼中可以看出getData方法的實現返回錯誤異常,實際上它作爲一個抽象方法,不需要實現任何東西;這裏displayBlock方法中調用它的抽象方法,這裏就是典型的抽象方法模式,以備於它的子類繼承實現它的抽象方法;

現在看看它的子類有哪些:

function BlueLMRBlock(){
}
inheritClass(BlueLMRBlock, LMRBlock); 
//繼承LMRBlock抽象類
BlueLMRBlock.prototype.getData = function() { //父類抽象方法的具體實現
    $(".tabs-nav a, .tabs-nav a span").css({"background-image":"url(script/tab/tab_blue.png)"});
    
this.color = "blue";
}

function GreenLMRBlock(){
}
inheritClass(GreenLMRBlock, LMRBlock);
GreenLMRBlock.prototype.getData 
= function() {
    $(
".tabs-nav a, .tabs-nav a span").css({"background-image":"url(script/tab/tab_green.png)"});
    
this.color = "green";
}

function RedLMRBlock(){
}
inheritClass(RedLMRBlock, LMRBlock);
RedLMRBlock.prototype.getData 
= function() {
    $(
".tabs-nav a, .tabs-nav a span").css({"background-image":"url(script/tab/tab_red.png)"});
    
this.color = "red";
}

function VioletLMRBlock(){
}
inheritClass(VioletLMRBlock, LMRBlock);
VioletLMRBlock.prototype.getData 
= function() {
    $(
".tabs-nav a, .tabs-nav a span").css({"background-image":"url(script/tab/tab_violet.png)"});
    
this.color = "violet";
}

這裏包括4種顏色的子類,全部都繼承於抽象類LMRBlock,子類中getData的方法做爲抽象方法的具體實現;

7. 現在該創建個工廠來實現它們了,添加BlockFactory.js文件,首先創建佈局抽象工廠類:

function BlockFactory(){
}
BlockFactory.prototype 
= {
    getBlock : 
function(block) {
        
var _block;
        _block 
= this.createBlock(block);
        
        
// 添加其他邏輯
        // 
        
        
return _block;
    },
    createBlock : 
function(block) {
        
return new Error("抽象方法,不能調用");
    },
    getBlockText : 
function() {
        
return new Error("抽象方法,不能調用");
    }
};

這裏createBlock和getBlockText同樣是做爲抽象方法;
現在要創建三個繼承於這個佈局抽象工廠類,LMRBlockFactory,LRMBlockFactory,MLRBlockFactory,這裏同樣以LMRBlockFactory爲例:

function LMRBlockFactory(){ 
}
inheritClass(LMRBlockFactory,BlockFactory);
LMRBlockFactory.prototype.createBlock 
= function(block) {
    
var _block;
    
switch(block)
    {
        
case "blue": _block = new BlueLMRBlock(); break;
        
case "red": _block = new RedLMRBlock(); break;
        
case "green": _block = new GreenLMRBlock(); break;
        
case "violet": _block = new VioletLMRBlock(); break;
        
default: _block = new BlueLMRBlock(); break;
    }
    
return _block;
};
LMRBlockFactory.prototype.getBlockText 
= function() {
    
return "LMR";
};

LMRBlockFactory繼承於佈局抽象工廠類,實現它的抽象方法createBlock和getBlockText,其中creatBlock通過參數值,創建對應的佈局實現類,這裏用到了典型的抽象工廠模式

8. 好了,一切都具備好了,現在開始討論我們的前臺使用了,添加factory.html,引用該引用的JS文件,這裏列出一些核心代碼:

1) 添加初始化數據

var membertype = "superadmin"//從用戶類型得到值,這裏是個假設,包含三個類型用戶:guest,blogmember,superadmin
var color = "blue"//這裏是初始化顏色,包括四種顏色:blue,green,red,violet
var blockfactory; //佈局工廠類的全局變量聲明

(你可以通過用戶登錄將登錄信息存入cookies中,從cookies獲取用戶類型和用戶選擇色調)

2) 初始化用戶類型

// 初始化用戶類型
var member = MemberFactory.createMemberType(membertype);
$(
"#spanMemberType").html(member.getMemberInfo());
$(
"#container-1 li[id^='li']").css("display","block");
$(
"#container-1 li[id^='li']").each(function(index){
    
var arr = $(this).attr("power").split('|'); //取得對應標籤的權限數組
    if(arr.indexOf(membertype) == -1//權限比較
    {
        $(
this).css("display","none");
        $(
"#fragment-" + (index+1)).css("display","none");
    }
 });
if(member.isSetColorBlock()) //是否可設置佈局和顏色
    $("#Set").css("display","block");
else
    $(
"#Set").css("display","none");
$(
"#selMemberType").val(membertype);

通過var member = MemberFactory.createMemberType(membertype);獲取用戶對象,通過用戶對象判斷是否可設置佈局和顏色;

3) 初始化版塊類型和顏色類型

// 初始化版塊類型和顏色類型
blockfactory = new LMRBlockFactory();
var block = blockfactory.getBlock(color);
block.displayBlock();
$(
"img[id^='imgcolor_']").removeClass().addClass("color-unselected");
$(
"#imgcolor_" + color).removeClass().addClass("color-selected");

通過創建工廠對象,從顏色中獲取佈局實現類的對象,然後通過調用displayBlock方法的實現初始化界面的佈局;

前臺JS代碼完整實現如下:

前臺JS代碼完整實現

 

至此,抽象工廠和工廠方法模式的一些思路已經應用在該博客系統中。

 這裏實現鏈接實例:

抽象工廠和工廠方法模式Demo

源代碼就不提供下載了,無非就是html,js,css文件,從鏈接實例中可以查看源代碼;

 總結

該篇文章用Javascript設計抽象工廠和工廠方法模式的思路,實現一個博客系統的構想。

發佈了19 篇原創文章 · 獲贊 1 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章