1. 程式人生 > >JavaScript設計模式----1

JavaScript設計模式----1

建立型設計模式
1、簡單工廠模式:
又叫靜態工廠方法,由一個工廠物件決定建立某一種產品物件類的例項,主要用來建立同一類物件。
很多剛剛瞭解面向物件之後會有很多盲區,例如一個關於登入表單驗證的例子:

處理前:

// 使用者名稱校驗
var LoginAlert = function (text) {
    this.content = text;
};
LoginAlert.prototype.show = function () {
// 顯示警示框
};
var userNameAlert = new LoginAlert('使用者名稱不能多於16個字母或數字');
userNameAlert.show();
// 密碼校驗
var passwordAlert = new LoginAlert('輸入密碼不正確');
passwordAlert.show();
// 添加註冊按鈕
var loginConfirm = function (text) {
    this.content = text;
};
loginConfirm.prototype.show = function () {
// 顯示確認框
};
var loginFailConfirm = new loginConfirm('您的使用者名稱不存在,請重新輸入!');
loginFailConfirm.show();
// 友好自定義提示框
var LoginPrompt = function (text) {
    this.content = text;
};
LoginPrompt.prototype.show = function () {
// 顯示提示框
};

處理後:

function createPop (type, text) {
    // 建立一個物件,並對物件拓展屬性和方法
    var o = new Object();
    o.content = text;
    o.show = function () {};
    if (type === 'alert') {
        // 警示框差異部分
    }
    if (type === 'prompt') {
        // 警示框差異部分
    }
    if (type === 'confirm') {
        // 警示框差異部分
    }
    // 將物件返回
    return o;
};
var userNameAlert = createPop('alert', '使用者名稱不能多於16個字母或數字');                

團隊專案開發不同於個人開發,其對全域性變數的限制很大,所以我們要儘量少建立全域性變數。對於同一類物件在不同需求中的重複性使用,很多
時候不需要重複建立,程式碼複用是面向物件程式設計的一條準則。通過對簡單工廠來建立一些物件,可以讓這些物件公用一些資源而又私有一些資源
,這是一種很不錯的實踐。不過對於簡單工廠模式,它的使用場合通常也就限制在建立單一物件。

2、工廠方法模式:
通過對產品類的抽象使其建立業務主要負責用於建立多類產品的例項

處理前:

// 建立Java學科類
var Java = function (content) {
    // 將內容儲存在content裡面 以備後日使用
    this.content = content;
    // 建立物件時,通過閉包直接執行,將內容按需求的樣式插入到頁面內
    (function (content) {
        var div = document.createElement('div');
        div.innerHTML = content;
        div.style.color = 'green';
        document.getElementById('container').appendChild(div);
    })(content);
};
// 建立PHP學科類
var Php = function (content) {
    // 將內容儲存在content裡面 以備後日使用
    this.content = content;
    // 建立物件時,通過閉包直接執行,將內容按需求的樣式插入到頁面內
    (function (content) {
        var div = document.createElement('div');
        div.innerHTML = content;
        div.style.color = 'yellow';
        div.style.background = 'red';
        document.getElementById('container').appendChild(div);
    })(content);
};
// 建立Javascript學科類
var JavaScript = function (content) {
    // 將內容儲存在content裡面 以備後日使用
    this.content = content;
    // 建立物件時,通過閉包直接執行,將內容按需求的樣式插入到頁面內
    ( function (content) {
        var div = document.createElement('div');
        div.innerHTML = content;
        div.style.background = 'pink';
        document.getElementById('container').appendChild(div);
    })(content);
};
// 學科類工廠
function JobFactory (type, content) {
    switch (type) {
    case 'Java':
        return new Java(content);
    case 'Php':
        return new Php(content);
    case 'Javascript':
        return new JavaScript(content);
    };
}

處理後:

// 安全模式建立工廠類
var Factory = function (type, content) {
    if (this instanceof Factory) {
        var s = new this[type](content);
        return s;
    } else {
        return new Factory(type, content);
    }
};
// 工廠原型中設定建立所有型別資料物件的基類
Factory.prototype = {
    Java: function (content) {
    //.....
    },
    Php: function (content) {
    //.....
    },
    JavaScript: function (content) {
    //.....
    },
    UI: function (content) {
        // 將內容儲存在content裡面 以備後日使用
        this.content = content;
        // 建立物件時,通過閉包直接執行,將內容按需求的樣式插入到頁面內
        (function (content) {
            var div = document.createElement('div');
            div.innerHTML = content;
            div.style.background = 'pink';
            div.style.border = '1px soild red';
            document.getElementById('container').appendChild(div);
        })(content);
    },
};

對於建立多類物件,前面學過的簡單工廠模式就不太適合了,這是簡單工廠模式的應用侷限,當然這正是工廠方法模式的價值所在,通過工廠方法模式
我們可以輕鬆建立多個類的例項物件,這樣工廠方法物件在建立物件的方式也避免了使用者與物件之間的耦合,使用者不必關心建立該物件的具體類,只
需要呼叫工廠方法即可。

3、抽象工廠模式:
通過對類的工廠抽象使其業務用於對產品類的建立,而不負責建立某一類產品的例項。

舉例:

// 汽車抽象類,當使用其例項物件的方法時會丟擲錯誤
var Car = function () {};
Car.prototype = {
    getPrice: function () {
        return new Error('抽象方法不能呼叫');
    },
    getSpeed: function () {
        return new Error('抽象方法不能呼叫');
    }
};

我們看到我們建立的這個car類其實什麼都做不了,建立時沒有任何屬性,然而原型prototype上的方法也不能使用,否則會報錯。但在繼承上卻是非常
有用,因為定義了一種類並且定義了該類所必備的方法,如果在子類中沒有重寫這些方法,那麼當呼叫的時候就會報錯,這一特點是很必要的,因為在一
些大型應用中,總會有一些子類去繼承另一些父類,這些父類經常會定義一些必要的方法,卻沒有具體的實現,如car類中的 getPrice()和getSpeed()
方法,那麼一旦子類建立了一物件,該物件總是應該具備一些方法的,如果這些方法從父類繼承過來就沒有具體的實現,那麼例項化物件便會呼叫父類中
的這些方法,如果父類能有一個好的提示,那麼對於忘記重寫子類的這些錯誤遺漏的避免是很有幫助的,這也是抽象類的一個作用,即定義一個產品蔟,
並宣告一些方法,如果子類中沒有重寫就會丟擲錯誤。

// 抽象方法
var VehicleFactory = function (subType, superType) {
    // 判斷抽象工廠中是否有該抽象類
    if (typeof VehicleFactory[superType] === 'function') {
        // 快取類
        function F () {};
        // 繼承父類屬性和方法
        F.prototype = new VehicleFactory[superType]();
        // 將子類constructor指向子類
        subType.constructor = subType;
        // 子類原型繼承父類
        subType.prototype = new F();
    } else {
        // 不存在該抽象類丟擲錯誤
        throw new Error('未建立該抽象類');
    }
};
// 小汽車抽象類
VehicleFactory.Car = function () {
    this.type = 'car';
};
VehicleFactory.Car.prototype = {
    getPrice: function () {
        return new Error('抽象方法不能呼叫');
    },
    getSpeed: function () {
        return new Error('抽象方法不能呼叫');
    }
};
// 寶馬汽車子類
var BMW = function (price, speed) {
    this.price = price;
    this.speed = speed;
};
// 抽象工廠實現對Car抽象類的繼承
VehicleFactory(BMW, 'Car');
BMW.prototype.getPrice = function () {
    return this.price;
};
BMW.prototype.getSpeed = function () {
    return this.speed;
};

// 掉用
var bmw = new BMW(100000, 1000);
console.log(bmw.getPrice); // 100000
console.log(bmw.getSpeed); // 1000

抽象工廠模式是設計模式中最抽象的一種,也是建立模式中唯一一種抽象化建立模式,該模式創建出的結果不是一個真實的物件例項,而是一個類簇,它制
定了類的結構,這也是區別於簡單工廠模式建立單一物件,工廠方法模式建立多類物件。當然由於JavaScript中不支援抽象化建立與虛擬方法,所以導致
這種模式不能像其他面嚮物件語言中應用的那麼廣泛。

4、建立者模式:
將一個複雜物件的構建層與其表示層相互分離,同樣的構建過程可採用不同的表示

// 建立一位人類
var Human = function (param) {
    // 技能
    this.skill = param && param.skill || '保密';
    // 興趣愛好
    this.hobby = param && param.hobby || '保密';
};
// 類人原型方法
Human.prototype = {
    getSkill: function () {
        return this.skill;
    },
    getHobby: function () {
        return this.hobby;
    }
};
// 例項化姓名類
var Named = function (name) {
    var that = this;
    // 構造器
    // 建構函式解析姓名與名
    (function (name, that) {
        that.wholeName = name;
       if (name.indexOf(' ') > -1) {
            that.FirstName = name.slice(0, name.indexOf(' '));
            that.seconName = name.sile(name.indexOf(' '));
        }
    })(name, that);
};
// 例項化職位類
var Work = function (work) {
    var that = this;
    // 構造器
    // 建構函式中通過傳入的職位特殊來設定相應職位以及描述
    (function (work, that) {
        switch (work) {
        case 'code':
            that.work = '工程師';
            that.workDescript = '每天沉醉於程式設計';
            break;
        case 'UI':
        case 'UE':
            that.work = '設計師';
            that.workDescript = '設計更似一種藝術';
            break;
        case 'teach':
            that.work = '教師';
            that.workDescript = '分享也是一種快樂';
            break;
        default:
            that.work = work;
            that.workDescript = '對不起,我們還不清楚您所選擇職位的相關描述';
        }
    })(work, that);
};
// 更換期望的職位
Work.prototype.changeWork = function (work) {
    this.work = work;
};
// 新增對職位的描述
Work.prototype.changeDescript = function (setence) {
    this.workDescript = setence;
};
var Person = function (name, work) {
    // 建立應聘者物件
    var _preson = new Human();
    // 建立應聘者姓名解析物件
    _preson.name = new Named(name);
    // 建立應聘者期望職位
    _person.work = new Work(work);
    // 將建立的應聘者物件返回
    return _preson;
};
使用:
var person = new Person('xiao ming', 'code');
console.log(person.skill); // 保密
console.log(person.name.FirstName); // xiao
console.log(person.work.work); // 工程師
console.log(person.work.workDescript); // 每天在程式設計中度過
person.work.changeDescript('更改一下職位描述!');
console.log(person.work.workDescript); // 更改一下職位描述    

5、原型模式:
用原型例項指向建立物件的類,使用與建立新的物件的類共享原型物件的屬性和方法。

處理前:

// 圖片輪播類
var LoopImages = function (imgArr, container) {
    this.imagesArray = imgArr;
    this.container = container;
    this.createImage = function () {};
    this.changeImage = function () {};
};
// 漸隱切換類
var FadeLoopImage = function (imgArr, container, arrow) {
    LoopImages.call(this, imgArr, container);
    // 切換箭頭私有變數
    this.arrow = arrow;
    this.changeImage = function () {
        console.log('FadeLoopImage changeImage function');
    };
};
// 例項化一個漸隱切換圖片類
var fadeImg = new FadeLoopImage(['01.jpg', '02.jpg', '03.jpg', '04.jpg'], 'slide', ['left.jpg', 'right.jpg']);

處理後:

// 圖片輪播類
var LoopImage = function (imgArr, container) {
    this.imagesArray = imgArr; // 輪播圖片陣列
    this.container = container; // 輪播圖片容器
};
// 漸隱切換類
var FadeLoopImage = function (imgArr, container, arrow) {
    LoopImages.call(this, imgArr, container);
    // 切換箭頭私有變數
    this.arrow = arrow;
};
FadeLoopImage.prototype = new LoopImages();
FadeLoopImage.prototype.changeImage = function () {
    console.log('FadeLoopImage changeImage function');
};
// 測試用例
console.log(fadeImg.container); // slide
fadeImg.changeImage(); // FadeLoopImage changeImage function
// 原型繼承
function prototypeExtend () {
    var F = function () {}; // 快取類,為例項化返回物件臨時建立
    args = arguments,
    i = 0,
    len = args.length;
    for (; i< lenl i++) {
        // 遍歷每個模板物件的屬性
        for (var j in args[i]) {
            // 將這些屬性賦值到快取類原型中
            F.prototype[j] = args[i][j];
        }
    }
    // 返回快取類的一個例項
    return new F();
};
var penguin = prototypeExtend({
    speed: 20,
    swim: function () {
        console.log('游泳速度' + this.spee)
    }
},{
    run: function () {
        console.log('奔跑速度' + speed)
    }
},{
    jump: function () {
        console.log('跳躍動作')
    }
});
penguin.swim(); // 游泳速度20
penguin.run(10); // 奔跑速度10
penguin.jumo(); // 跳躍動作

6、單例模式:
又稱為單體模式,是隻允許例項化一次的物件類,有時我們也用一個物件來規劃一個名稱空間,井井有條的管理物件上的屬性和方法。
案例:

// 惰性載入單例
var LazySingle = (function () {
    // 單例例項引用
    var _instance = null;
    // 單例
    function Single () {
        // 這裡定義了私有的屬性和方法
        return {
            publicMethod: function () {},
            publicProperty: '1.0'
        };
    };
    //獲取單例物件介面
    return function () {
        // 如果為建立單例建立單例
        if (!_instance) {
            _instance = Single();
        };
        // 返回單例
        return _instance;
    };	
})();
測試用例:
console.log(LazySingle().publicProperty); // 1.0

單例模式有時也被稱為單體模式,它只是一個只允許例項化一次的物件類,有時這麼做也是為了節省系統資源。當然Javascript中單例模式經常
作為名稱空間物件實現,通過單例物件我們可以將各個模組的程式碼井井有條的梳理在一起。