1. 程式人生 > >JavaScript設計模式(3)

JavaScript設計模式(3)

reader change 引用 sage 方法 易懂 factor bject fetch

工廠模式

1. 簡單工廠

簡單工廠:使用一個類或對象封裝實例化操作

var Speedster = function() {}
Speedster.prototype = {
    assemble: function() {}
}

var ComfortCruiser = function() {}
ComfortCruiser.prototype = {
    assemble: function() {}
}

// 簡單工廠模式
var BicycleFactory = {
    createBicycle: function(model) {
        var bicycle;
switch(model) { case ‘The Speedster‘: bicycle = new Speedster(); break; case ‘The Comfort Cruiser‘: default: bicycle = new ComfortCruiser() } //檢查是否實現了相應父類的方法 // Interface.ensureImplement(bicycle, Bicycle)
return bicycle } } var BicycleShop = function() {}; BicycleShop.prototype = { sellBicycle: function(model) { var bicycle = BicycleFactory.createBicycle(model); bicycle.assemble() return bicycle } } // usage var shop = new BicycleShop() var b1 = shop.sellBicycle
(‘aaaa‘) // 可提供車型的所有信息都集中在一個地方管理

2. 工廠模式

這裏的工廠指的是一個將其成員對象的實例化推遲到子類中進行的類

var AcmeSpeedster = function() {}
AcmeSpeedster.prototype = {
    assemble: function() {}
}
var AcmecomfortCruiser = function() {}
AcmecomfortCruiser.prototype = {
    assemble: function() {}
}

// BicycleShop 就是一個工廠,它是一個抽象類,需要在繼承後實現裏面用於實例化的工廠方法
var BicycleShop = function() {};
BicycleShop.prototype = {
    sellBicycle: function(model) {
        var bicycle = this.createBicycle(model)
        bicycle.assemble()
        return bicycle;
    },
    // 工廠方法
    createBicycle: function(model) {
        throw new Error(‘Unsupported operation on an abstract class.‘)
    }
}

var AcmeBicycleShop = function() {};
extend(AcmeBicycleShop, BicycleShop);
AcmeBicycleShop.prototype.createBicycle = function(model) {
    var bicycle;
    switch(model) {
        case ‘The Speedster‘:
            bicycle = new AcmeSpeedster();
            break;
        case ‘The Comfort Cruiser‘:
        default:
            bicycle = new AcmecomfortCruiser();
    }
    // Interface.ensureImplement(bicycle, Bicycle)
    return bicycle
}

// usage
var ashop = new AcmeBicycleShop()
ashop.sellBicycle()

// 對 Bycycle 進行一般性操作的代碼可以全部寫在父類 BicycleShop 中,而具體的 Bicycle 對象進行實例化的工作則別留到子類中。

3. 示例:XHR工廠

var SimpleHandle = function() {};
SimpleHandle.prototype = {
    require: function(method, url, callback, postVars) {
        var xhr = this.createXhrObject();
        xhr.onreadystatechange = function() {
            if(xhr.readyState !== 4) return;
            (xhr.status === 200) ?
            callback.success(xhr.responseText, xhr.responseXML):
            callback.failure(xhr.status);
        }
        xhr.open(method, url, true);
        if(method != ‘POST‘) postVars = null;
        xhr.send(postVars)
    },
    createXhrObject: function() {
        var methods = [
            function() { return new XMLHttpRequest() },
            function() { return new ActiveXObject(‘Msxml2.XMLHTTP‘) },
            function() { return new ActiveXObject(‘Microsoft.XMLHTTP‘) }
        ];
        for(var i=0, len=methods.length; i<len; i++) {
            try {
                methods[i]();
            } catch(err) {
                continue;
            }

            // memoizing : 返回所創建的對象並將自身改為用以創建那個對象的函數
            this.createXhrObject = methods[i]   
            return methods[i]()
        }

        throw new Error(‘SimpleHandle: Could not create an XHR object.‘)
    }
}

# usage
var myHandler = new SimpleHandle();
var callback = {
    success: function(responseText) {alert(‘Success: ‘ + responseText)},
    failure: function(statusCode) {alert(‘Failure: ‘ + statusCode)}
}
myHandler.require(‘POST‘, ‘http://www.baidu.com‘, callback)

4. 示例 :RSS閱讀器

var SimpleHandle = function() {};
SimpleHandle.prototype = {
    require: function(method, url, callback, postVars) {
        var xhr = this.createXhrObject();
        xhr.onreadystatechange = function() {
            if(xhr.readyState !== 4) return;
            (xhr.status === 200) ?
            callback.success(xhr.responseText, xhr.responseXML):
            callback.failure(xhr.status);
        }
        xhr.open(method, url, true);
        if(method != ‘POST‘) postVars = null;
        xhr.send(postVars)
    },
    createXhrObject: function() {
        var methods = [
            function() { return new XMLHttpRequest() },
            function() { return new ActiveXObject(‘Msxml2.XMLHTTP‘) },
            function() { return new ActiveXObject(‘Microsoft.XMLHTTP‘) }
        ];
        for(var i=0, len=methods.length; i<len; i++) {
            try {
                methods[i]();
            } catch(err) {
                continue;
            }

            // memoizing : 返回所創建的對象並將自身改為用以創建那個對象的函數
            this.createXhrObject = methods[i]   
            return methods[i]()
        }

        throw new Error(‘SimpleHandle: Could not create an XHR object.‘)
    }
}

var ListDisplay = function(id, parent) {
    this.list = document.createElement(‘ul‘);
    this.list.id = id;
    parent.appendChild(this.list)
}
ListDisplay.prototype = {
    append: function(text) {
        var newEl = document.createElement(‘li‘);
        this.list.appendChild(newEl);
        newEl.innerHTML = text;
        return newEl
    },
    remove: function(el) {
        this.list.removeChild(el);
    },
    clear: function() {
        this.list.innerHTML = ‘‘;
    }
}

var FeedReader = function(display, xhrHandler, conf) {
    this.display = display;
    this.xhrHandler = xhrHandler;
    this.conf = conf;

    this.startUpdates();
}
FeedReader.prototype = {
    fetchFeed: function() {
        var that = this;
        var callback = {
            success: function(text, xml) {
                that.parseFeed(text, xml)
            },
            failure: function(status) {
                that.showError(status)
            }
        }
        this.xhrHandler.require(‘GET‘, this.conf.feedUrl, callback);
    },
    parseFeed: function(responseText, responseXML) {
        this.display.clear()
        var items = responseXML.getElementsByTagName(‘item‘);
        for(var i=0, len=items.length; i<len; i++) {
            var title = items[i].getElementsByTagName(‘title‘)[0];
            var link = items[i].getElementsByTagName(‘link‘)[0];
            this.display.append(‘<a href="‘ + link.firstChild.data + ‘">‘+ title.firstChild.data + ‘</a>‘ ) 
        }
    },
    showError: function(statusCode) {
        this.display.clear();
        this.display.append(‘Error fetching feed.‘)
    },
    stopUpdates: function() {
        clearInterval(this.interval);
    },
    startUpdates: function() {
        this.fetchFeed();
        var that = this;
        this.interval = setInterval(function() {
            that.fetchFeed();
        }, this.conf.updateInterval * 1000)
    }
}

var FeedManager = {
    createFeedReader: function(conf) {
        var displayModule = new ListDisplay(conf.id + ‘-display‘, conf.parent)
        // Interface.ensureImplements(displayModule, DisplayModule)

        var xhrHandler = new SimpleHandle()
        // Interface.ensureImplements(xhrHandler, AjaxHandler)

        return new FeedReader(displayModule, xhrHandler, conf)
    }
}

var conf = {
    id: ‘cnn-top-stories‘,
    feedUrl: ‘https://laike9m.com/blog/rss/‘, //http://www.adaymag.com/feed/‘,
    updateInterval: 60,
    parent: document.getElementById(‘feed-readers‘)
}

// usage
FeedManager.createFeedReader(conf)

5. 工廠模式的適用場合

  • 動態實現
    • 創建一些用不同方法實現統一接口的對象(如上面自行車例子)
    • 可明確的實現統一接口【自行車】,也可隱含的實現(根據網速等環境原因) 【XHR 工廠】
  • 節省設置開銷
    • 把實例化對象時重復的操作集中在一個地方
  • 用許多小型對象組成一個大對象 【FeedMabage】

6. 工廠模式之弊

  • 如果不會另外更換一個類,或者不需要在運行期間在一系列可互換的類中進行選擇,那就不應該使用工廠方法
  • 使用 new 實例化可以讓代碼更簡單易懂
  • 切勿濫用,如果拿不定主意,那就不要用,因為在重構代碼的時候還有機會使用

註意

轉載、引用,但請標明作者和原文地址

JavaScript設計模式(3)