日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

JavaScript設(shè)計(jì)模式

 威震書館 2016-04-14

1.弱類型語言

  • 在JavaScript中,定義變量時(shí)不必聲明其類型。但這并不意味著變量沒有類型。一個(gè)變量可以屬于幾種類型之一,這取決于其包含的數(shù)據(jù)。JavaScript中有三種原始類型:布爾型、數(shù)值型和字符串類型(不區(qū)分整數(shù)和浮點(diǎn)數(shù)是JavaScript與大多數(shù)其他主流語言的一個(gè)不同之處)。此外,還有對(duì)象類型和包含可執(zhí)行代碼的函數(shù)類型,前者是一種復(fù)合數(shù)據(jù)類型(數(shù)組是一種特殊的對(duì)象,它包含著一批值的有序集合)。最后,還有空類型(null)未定義類型(undefined)這兩種數(shù)據(jù)類型。原始數(shù)據(jù)類型按值傳送,而其他數(shù)據(jù)類型則按引用傳送。
  • 與其他弱類型語言一樣,JavaScript中的變量可以根據(jù)所賦的值改變類型。原始類型之間也可以進(jìn)行類型轉(zhuǎn)換。toString可以把數(shù)值或布爾值轉(zhuǎn)為字符串。parseFloat和parseInt函數(shù)可以把字符串轉(zhuǎn)變?yōu)閿?shù)值。雙重“非”可以把字符串或數(shù)值轉(zhuǎn)變?yōu)椴紶栔担?code>var bool = !!num;

2.初談閉包

匿名函數(shù)最有趣的用途是用來創(chuàng)建閉包。閉包是一個(gè)受到保護(hù)的變量空間,由內(nèi)嵌函數(shù)生成。JavaScript具有函數(shù)級(jí)的作用域。這意味著定義在函數(shù)內(nèi)部的變量在函數(shù)外部不能被訪問。JavaScript的作用域又是詞法性質(zhì)的。這意味著函數(shù)運(yùn)行在定義它的作用域中,而不是在調(diào)用它的作用域中。把這兩個(gè)因素結(jié)合起來,就能通過把變量包裹在匿名函數(shù)中而對(duì)其加以保護(hù)。

3.依賴于接口的設(shè)計(jì)模式

下面列出的設(shè)計(jì)模式,尤其依賴接口:

  • 工廠模式。對(duì)象工廠所創(chuàng)建的具體對(duì)象會(huì)因具體情況而異。使用接口可以確保所創(chuàng)建出來的這些對(duì)象可以互換使用。也就是說,對(duì)象工廠可以保證其生產(chǎn)出來的對(duì)象都實(shí)現(xiàn)了必需的方法。
  • 組合模式。如果不用接口你就不可能用這個(gè)模式。組合模式的中心思想在于可以將對(duì)象群體與其組成對(duì)象同等對(duì)待。這是通過讓它們實(shí)現(xiàn)同樣的接口來做到的。如果不進(jìn)行某種形式的鴨式辨型或類型檢查,組合模式就會(huì)失去大部分作用。
  • 裝飾者模式。裝飾者通過透明地為另一對(duì)象提供包裝而發(fā)揮作用。這是通過實(shí)現(xiàn)與另外那個(gè)對(duì)象完全相同的接口而做到的。對(duì)于外界而言,一個(gè)裝飾者和它所包裝的對(duì)象看不出有什么區(qū)別。
  • 命令模式。代碼中所有的命令對(duì)象都要實(shí)現(xiàn)同一批方法。通過使用接口,你為執(zhí)行這些命令對(duì)象而創(chuàng)建的類可以不必知道這些對(duì)象具體是什么,只要知道它們都實(shí)現(xiàn)了正確的接口即可。

4.用命名規(guī)范區(qū)別私用成員

在一些方法和屬性的名稱前面加下劃線以示其私用性。下劃線的這種用法是一個(gè)眾所周知的命名規(guī)范,它表明一個(gè)屬性(或方法)僅供對(duì)象內(nèi)部使用,直接訪問它或設(shè)置它可能會(huì)導(dǎo)致意想不到的后果。這有助于防止程序員對(duì)它的無意使用,卻不能防止對(duì)它的有意使用。后一個(gè)目標(biāo)的實(shí)現(xiàn)需要有真正私用性的方法。

5.作用域

下面這個(gè)示例說明了JavaScript中作用域的特點(diǎn):

function foo() {    var a = 10;    function bar() {        a *= 2;    }    bar();    return a;}

在這個(gè)示例中,a定義在函數(shù)foo中,但函數(shù)bar可以訪問它,因?yàn)閎ar也定義在foo中。bar在執(zhí)行過程中將a設(shè)置為a乘以2。當(dāng)bar在foo中被調(diào)用時(shí)它能夠訪問a,這可以理解。但是如果bar是在foo外部被調(diào)用呢?

function foo() {    var a = 10;    function bar() {        a *= 2;        return a;    }    return bar;}var baz = foo();console.log(baz());//20console.log(baz());//40console.log(baz());//80var blat = foo();console.log(blat());//20

在上述代碼中,所返回的對(duì)bar函數(shù)的引用被賦給變量baz。這個(gè)函數(shù)現(xiàn)在是在foo外部被調(diào)用,但它依然能夠訪問a。這是因?yàn)镴avaScript的作用域是詞法性的。函數(shù)是運(yùn)行在定義它們的作用域中(本例中是foo內(nèi)部的作用域),而不是運(yùn)行在調(diào)用它們的作用域中。只要bar被定義在foo中,它就能訪問在foo中定義的所有變量,即使foo的執(zhí)行已經(jīng)結(jié)束。
這就是閉包的一個(gè)例子。在foo返回后,它的作用域被保存下來,但只有它返回的那個(gè)函數(shù)能夠訪問這個(gè)作用域。在前面的示例中,baz和blat各有這個(gè)作用域及a的一個(gè)副本,而且只有它們自己能對(duì)其進(jìn)行修改。返回一個(gè)內(nèi)嵌函數(shù)是創(chuàng)建閉包最常用的手段。

6.用閉包實(shí)現(xiàn)私用成員的弊端

在門戶打開型對(duì)象創(chuàng)建模式中,所有方法都創(chuàng)建在原型對(duì)象中,因此不管派生多少對(duì)象實(shí)例,這些方法在內(nèi)存中只存在一份。而包含特權(quán)方法、私用成員的創(chuàng)建模式中,每生成一個(gè)新的對(duì)象示例都將為每一個(gè)私用方法和特權(quán)方法生成一個(gè)新的副本。這會(huì)比其他做法耗費(fèi)更多內(nèi)存,所以只宜用在需要真正的私用成員的場(chǎng)合。這種對(duì)象創(chuàng)建模式也不利于派生子類,因?yàn)樗缮龅淖宇惒荒茉L問超類的任何私用屬性或方法。相比之下,在大多數(shù)語言中,子類都能訪問超類的所有私有屬性和方法。故在JavaScript中用閉包實(shí)現(xiàn)私用成員導(dǎo)致的派生問題稱為“繼承破壞封裝”。

7.靜態(tài)方法和屬性

前面所講的作用域和閉包的概念可用于創(chuàng)建靜態(tài)成員,包括公用和私用的。大多數(shù)方法和屬性所關(guān)聯(lián)的是類的實(shí)例,而靜態(tài)成員所關(guān)聯(lián)的則是類本身。換句話說,靜態(tài)成員是在累的層次上操作,而不是在實(shí)例的層次上操作。每個(gè)靜態(tài)成員都只有一份。稍后將會(huì)看到,靜態(tài)成員是直接通過類對(duì)象訪問的。
下面是添加了靜態(tài)屬性和方法的Book類:

var Book = (function () {    //私有靜態(tài)變量    var numOfBooks = 0;    //私有靜態(tài)方法    function checkIsbn(isbn) {    }    //返回一個(gè)構(gòu)造器    return function (newIsbn, newTitle, newAuthor) {        //私有屬性        var isbn, title, author;        //特權(quán)方法        this.getIsbn = function () {            return isbn;        };        this.setIsbn = function (newIsbn) {            if (!checkIsbn(newIsbn)) {                throw new Error('Book: Invalid ISBN.');            }            isbn = newIsbn;        };        this.getTitle = function () {            return title;        };        this.setTitle = function (newTitle) {            title = newTitle || 'No title specified';        };        this.getAuthor = function () {            return author;        };        this.setAuthor = function (newAuthor) {            author = newAuthor || 'No author specified';        };        //Constructed code.        numOfBooks++;        if (numOfBooks > 50) {            throw new Error('.');        }        this.setIsbn(newIsbn);        this.setTitle(newTitle);        this.setAuthor(newAuthor);    }})();//公共靜態(tài)方法Book.convertToTitleCase = function (inputString) {};//公共非特權(quán)方法Book.prototype = {    display: function () {    }};

這里的私用成員和特權(quán)成員仍然被聲明在構(gòu)造器中(分別使用var和this關(guān)鍵字)。但哪個(gè)構(gòu)造器卻從原來的普通函數(shù)變成了一個(gè)內(nèi)嵌函數(shù),并且被作為包含它的函數(shù)的返回值賦給變量Book。這就創(chuàng)建了一個(gè)閉包,你可以把靜態(tài)的私用成員聲明在里面。位于外層函數(shù)聲明之后的一對(duì)空括號(hào)很重要,其作用是一段代碼載入就立即執(zhí)行這個(gè)函數(shù)(而不是在調(diào)用Book構(gòu)造函數(shù)時(shí))。這個(gè)函數(shù)的返回值是另一個(gè)函數(shù),它被賦給Book變量,Book因此成了一個(gè)構(gòu)造函數(shù)。在實(shí)例化Book時(shí),所調(diào)用的是這個(gè)內(nèi)層函數(shù)。外層那個(gè)函數(shù)只是用于創(chuàng)建一個(gè)可以用來存放靜態(tài)私用成員的閉包。

  • 在本例中,checkIsbn被設(shè)計(jì)為靜態(tài)方法 ,原因是為Book的每個(gè)實(shí)例都生成這個(gè)方法的一個(gè)新副本毫無道理。此外還有一個(gè)靜態(tài)屬性numOfBooks,其作用在于跟蹤Book構(gòu)造器的總調(diào)用次數(shù)。本例利用這個(gè)屬性將Book實(shí)例的個(gè)數(shù)限制為不超過50個(gè)。
  • 這些私用的靜態(tài)成員可以從構(gòu)造器內(nèi)部訪問,這意味著所有私用函數(shù)和特權(quán)函數(shù)都能訪問它們。與其他方法相比,它們有一個(gè)明顯的優(yōu)點(diǎn),那就是內(nèi)存中只會(huì)存放一份。因?yàn)槠渲心切╈o態(tài)方法被聲明在構(gòu)造器之外,所以它們不是特權(quán)方法,不能訪問任何定義在構(gòu)造器中的私用屬性。定義在構(gòu)造器中的私用方法能夠調(diào)用那些私用靜態(tài)方法,反之則不然。要判斷一個(gè)私用方法是否應(yīng)該被設(shè)計(jì)為靜態(tài)方法,一條經(jīng)驗(yàn)法則是看它是否需要訪問任何實(shí)例數(shù)據(jù)。如果它不需要,那么將其設(shè)計(jì)為靜態(tài)方法會(huì)更有效率(從內(nèi)存占用的意義上來講),因?yàn)樗粫?huì)被創(chuàng)建一份。
  • 創(chuàng)建公用的靜態(tài)成員則容易得多,只需直接將其作為構(gòu)造函數(shù)這個(gè)對(duì)象的屬性創(chuàng)建即可,前述代碼中的方法converToTitleCase就是一例。這實(shí)際上相當(dāng)于把構(gòu)造器作為命名空間來使用。
  • 所有公用靜態(tài)方法如果作為獨(dú)立的函數(shù)來聲明其實(shí)也同樣簡(jiǎn)單,但最好還是像這樣把相關(guān)行為集中在一起。這些方法用于與類這個(gè)整體相關(guān)的任務(wù),而不是與類的任一特定實(shí)例相關(guān)的任務(wù)。它們并不直接依賴于對(duì)象實(shí)例中包含的任何數(shù)據(jù)。

8.私用變量模仿常量

通過創(chuàng)建只有取值器而沒有賦值器的私用變量可以模仿常量。

var Class = (function () {    var UPPER_BOUND = 100;    //構(gòu)造器    var ctor = function (constructorArgument) {    };    //靜態(tài)特權(quán)方法    ctor.getUPPER_BOUND = function () {        return UPPER_BOUND;    };    return ctor;})();

9.封裝之弊

  • 私用方法很難進(jìn)行單元測(cè)試。因?yàn)樗鼈兗捌鋬?nèi)部變量都是私用的,所以在對(duì)象外部無法訪問到它們。這個(gè)問題沒有什么很好的應(yīng)對(duì)之策。你要么通過使用公用方法來提供訪問途徑(這樣一來就葬送了使用私有方法所帶來的大多數(shù)好處),要么設(shè)法在對(duì)象內(nèi)部定義并執(zhí)行所有單元測(cè)試。最好的解決辦法是只對(duì)公用方法進(jìn)行單元測(cè)試。這應(yīng)該能覆蓋到所有私用方法,盡管對(duì)它們的測(cè)試只是間接的。這種問題不是JavaScript所獨(dú)有的,只對(duì)公用方法進(jìn)行單元測(cè)試是一種廣為接收的處理方式。
  • 使用封裝意味著不得不與復(fù)雜的作用域鏈打交道。
  • 封裝可能會(huì)損害類的靈活性,致使其無法被用于某些你未曾想到過的目的。

10.單體模式

單體模式是JavaScript中最基本但又最有用的模式之一,它可能比其他任何模式都更常用。這種模式提供了一種將代碼組織為一個(gè)邏輯單元的手段,這個(gè)邏輯單元中的代碼可以通過單一的變量進(jìn)行訪問。通過確保單體對(duì)象只存在一份實(shí)例,你就可以確信自己的所有代碼使用的都是同樣的全局資源。
單體類在JavaScript中有許多用處。它們可以用來劃分命名空間,以減少網(wǎng)頁中全局變量的數(shù)目。更重要的是,借助于單體模式,你可以把代碼組織得更為一致,從而使其更容易閱讀和維護(hù)。

11.單體的基本結(jié)構(gòu)

var Singleton = {    attribute1: true,    attribute2: 10,    method1: function () {    },    method2: function (args) {    }};
  • 這個(gè)單體對(duì)象可以被修改。你可以為其添加新成員,這一點(diǎn)與別的對(duì)象字面量沒有什么不同。你也可以用delete運(yùn)算符刪除其現(xiàn)有成員。這實(shí)際上違背了面向?qū)ο笤O(shè)計(jì)的一條原則:類可以被擴(kuò)展,但不應(yīng)該被修改。
  • 按傳統(tǒng)的定義,單體是一個(gè)只能被實(shí)例化一次并且可以通過一個(gè)眾所周知的訪問點(diǎn)訪問的類。要是嚴(yán)格按照這個(gè)定義來說,前面的例子所示的并不是一個(gè)單體,因?yàn)樗皇且粋€(gè)可實(shí)例化的類。我們打算把單體模式定義的更廣義一些:?jiǎn)误w是一個(gè)用來劃分命名空間并將一批相關(guān)方法和屬性組織在一起的對(duì)象,如果可以被實(shí)例化,那么它只能被實(shí)例化一次。

12.劃分命名空間

為了避免無意中改寫變量,最好的解決辦法之一是用單體對(duì)象將代碼組織在命名空間之中。下面是前面的例子用單體模式改良后的結(jié)果:

var MyNamespace = {    findProduct: function (id) {    }};

現(xiàn)在findProduct函數(shù)是MyNamespace中的一個(gè)方法,它不會(huì)被全局命名空間中聲明的任何新變量改寫。要注意,該方法仍然可以從各個(gè)地方訪問。不同之處在于現(xiàn)在其調(diào)用方式不是findProduct(id),而是MyNamespace.findProduct(id)。還有一個(gè)好處就是,這可以讓其他程序員大體知道這個(gè)方法的聲明地點(diǎn)及其作用。用命名空間把類似的方法組織到一起,也有助于增強(qiáng)代碼的文檔性。

13.模塊模式

有一種單體模式被稱為模塊模式,因?yàn)樗梢园岩慌嚓P(guān)方法和屬性組織為模塊并起到劃分命名空間的作用。例如:

MyNamespace.Singleton = (function () {    //私有成員    var privateAttribute1 = false;    var privateAttribute2 = [1, 2, 3];    function privateMethod1() {    }    function privateMethod2() {    }    return {        //public members        publicAttribute1: true,        publicAttribute2: 10,        publicMethod1: function () {        },        publicMethod2: function (args) {        }    }})();

14.簡(jiǎn)單工廠模式

最好用一個(gè)例子來說明簡(jiǎn)單工廠模式的概念。假設(shè)你想開幾個(gè)自行車商店,每個(gè)店都有幾種型號(hào)的自行車出售。這可以用一個(gè)類來表示:

/*BicycleShop class.*/var BicycleShop = function () {};BicycleShop.prototype = {    sellBicycle: function (model) {        var bicycle;        switch (model) {            case 'The Speedster':                bicycle = new SpeedSter();                break;            case 'The Lowrider':                bicycle = new Lowrider();                break;            case 'The Comfort Cruiser':            default:                bicycle = new ComfortCruiser();        }        Interface.ensureImplements(bicycle, Bicycle);        bicycle.assemble();        bicycle.wash();        return bicycle;    }};

sellBicycle方法根據(jù)所要求的自行車型號(hào)用switch語句創(chuàng)建一個(gè)自行車的實(shí)例。各種型號(hào)的自行車實(shí)例可以互換使用,因?yàn)樗鼈兌紝?shí)現(xiàn)了Bicycle接口:

/* The Bicycle interface. */var Bicycle = new Interface('Bicycle', ['assemble', 'wash', 'ride', 'repair']);/* Speedster class. */var Speedster = function () {};Speedster.prototype = {    assemble: function () {    },    wash: function () {    },    ride: function () {    },    repair: function () {    }};

要出售某種型號(hào)的自行車,只要調(diào)用sellBicycle方法即可:

var californiaCruisers = new BicycleShop();var yourNewBike = californiaCruisers.sellBicycle('The Speedster');

在情況發(fā)生變化之前,這倒也挺管用。但要是你想在供貨目錄中加入一款新車型又會(huì)怎么樣呢?你得為此修改BicycleShop的代碼,哪怕這個(gè)類的實(shí)際功能實(shí)際上并沒有發(fā)生改變——依舊是創(chuàng)建一個(gè)自行車的新實(shí)例,組裝它,清洗它,然后把它交給顧客。更好的解決辦法是把sellBicycle方法中“創(chuàng)建新實(shí)例”這部分工作轉(zhuǎn)交給一個(gè)簡(jiǎn)單工廠對(duì)象:

/* BicycleFactory namespace. */var BicycleFactory = {    createBicycle:function(model){        var bicycle;        switch (model) {            case 'The Speedster':                bicycle = new SpeedSter();                break;            case 'The Lowrider':                bicycle = new Lowrider();                break;            case 'The Comfort Cruiser':            default:                bicycle = new ComfortCruiser();        }        Interface.ensureImplements(bicycle, Bicycle);        return bicycle;    }};

BicycleFactory是一個(gè)單體,用來把createBicycle方法封裝在一個(gè)命名空間中。這個(gè)方法返回一個(gè)實(shí)現(xiàn)了Bicycle接口的對(duì)象,然后你可以照常對(duì)其進(jìn)行組裝和清洗:

/* BicycleShop class, improved. */var BicycleShop = function () {};BicycleShop.prototype = {    sellBicycle: function (model) {        var bicycle = BicycleFactory.createBicycle(model);        bicycle.assemble();        bicycle.wash();        return bicycle;    }};

這個(gè)BicycleFactory對(duì)象可以供各種類用來創(chuàng)建新的自行車實(shí)例。有關(guān)可供車型的所有信息集中在一個(gè)地方管理 ,所以添加更多車型很容易:

/* BicycleFactory namespace,with more models. */var BicycleFactory = {    createBicycle: function (model) {        var bicycle;        switch (model) {            case 'The Speedster':                bicycle = new SpeedSter();                break;            case 'The Lowrider':                bicycle = new Lowrider();                break;            case 'The Flatlander':                bicycle = new Flatlander();                break;            case 'The Comfort Cruiser':            default:                bicycle = new ComfortCruiser();        }        Interface.ensureImplements(bicycle, Bicycle);        return bicycle;    }};

15.工廠模式

真正的工廠模式與簡(jiǎn)單工廠模式的區(qū)別在于,它不是另外使用一個(gè)類或?qū)ο髞韯?chuàng)建自行車,而是使用一個(gè)子類。按照正式定義,工廠是一個(gè)將其成員對(duì)象的實(shí)例化推遲到子類中進(jìn)行的類。

16.工廠模式的適用場(chǎng)合

  • 動(dòng)態(tài)實(shí)現(xiàn):如果需要?jiǎng)?chuàng)建一些用不同方式實(shí)現(xiàn)同一接口的對(duì)象,那么可以使用一個(gè)工廠方法或簡(jiǎn)單工廠對(duì)象來簡(jiǎn)化選擇實(shí)現(xiàn)的過程。
  • 節(jié)省設(shè)置開銷:如果對(duì)象需要進(jìn)行復(fù)雜并且彼此相關(guān)的設(shè)置,那么使用工廠模式可以減少每種對(duì)象所需的代碼量。如果這種設(shè)置只需要為特定類型的所有實(shí)例執(zhí)行一次即可,這種作用尤為突出。把這種設(shè)置代碼放到類的構(gòu)造函數(shù)中并不是一種高效的做法,這是因?yàn)榧幢阍O(shè)置工作已經(jīng)完成,每次創(chuàng)建新實(shí)例的時(shí)候這些代碼還是會(huì)執(zhí)行,而且這樣做會(huì)把設(shè)置代碼分散到不同的類中。工廠方法非常適合于這種場(chǎng)合。它可以在實(shí)例化所有需要的對(duì)象之前先一次性地進(jìn)行設(shè)置。無論有多少類會(huì)被實(shí)例化,這種辦法都可以讓設(shè)置代碼集中在一個(gè)地方。
  • 用許多小型對(duì)象組成一個(gè)大對(duì)象

17.工廠模式之利

  • 工廠模式的主要好處在于消除對(duì)象間的耦合。通過使用工廠方法而不是new關(guān)鍵字及具體類,你可以把所有實(shí)例化的代碼集中在一個(gè)位置。這可以大大簡(jiǎn)化更換所用的類或在運(yùn)行期間動(dòng)態(tài)選擇所用的類的工作。在派生子類時(shí)它也提供了更強(qiáng)大的靈活性。
  • 所有這些好處都與面向?qū)ο笤O(shè)計(jì)的這兩條原則有關(guān):弱化對(duì)象間的耦合;防止代碼的重復(fù)。在一個(gè)方法中進(jìn)行類的實(shí)例化,可以消除重復(fù)性的代碼。這是在用一個(gè)對(duì)接口的調(diào)用取代一個(gè)具體的實(shí)現(xiàn)。這些都有助于創(chuàng)建模塊化的代碼。

18.橋接模式

橋接模式最常見和實(shí)際的應(yīng)用場(chǎng)合之一就是事件監(jiān)聽器回調(diào)函數(shù)。假設(shè)有一個(gè)名為getBeerById的API函數(shù),它根據(jù)一個(gè)標(biāo)識(shí)符返回有關(guān)某種啤酒的信息。你希望用戶在點(diǎn)擊的時(shí)候獲取這種信息。那個(gè)被點(diǎn)擊的元素很可能有啤酒的標(biāo)識(shí)符信息,它可能是作為元素自身的ID保存,也可能是作為別的自定義屬性保存。下面是一種做法:

addEvent(element, 'click', getBeerById);function getBeerById(e) {    var id = this.id;    asyncRequest('GET', 'beer.uri?id=' + id, function (resp) {        console.log(resp.responseText);    });}

這個(gè)API只能工作在瀏覽器中,如果要對(duì)這個(gè)API函數(shù)做單元測(cè)試,或者在命令行中執(zhí)行,可能會(huì)報(bào)錯(cuò)。一個(gè)優(yōu)良的API設(shè)計(jì),不應(yīng)該把它與任何特定的實(shí)現(xiàn)攪在一起。

function getBeerById(id, callback) {    asyncRequest('GET', 'beer.uri?id=' + id, function (resp) {        callback(resp.responseText);    })}

現(xiàn)在我們將針對(duì)接口而不是實(shí)現(xiàn)進(jìn)行編程,用橋接模式把抽象隔離開來:

addEvent(element, 'click', getBeerByIdBridge);function getBeerBIdBridge(e) {    getBeerById(this.id, function (beer) {        console.log(beer);    });}

這下getBeerById并沒有和事件對(duì)象捆綁在一起了。

19.用橋接模式聯(lián)結(jié)多個(gè)類

var Class1 = function (a, b, c) {    this.a = a;    this.b = b;    this.c = c;};var Class2 = function (d) {    this.d = d;};var BridgeClass = function (a, b, c, d) {    this.one = new Class1(a, b, c);    this.two = new Class2(d);};

20.適配器模式

適配器模式可以用來在現(xiàn)有接口和不兼容的類之間進(jìn)行適配。使用這種模式的對(duì)象又叫包裝器,因?yàn)樗鼈兪窃谟靡粋€(gè)新的接口包裝另一個(gè)對(duì)象。

21.適配器的特點(diǎn)

  • 適配器可以被添加到現(xiàn)有代碼中以協(xié)調(diào)兩個(gè)不同的接口。如果現(xiàn)有代碼的接口能很好地滿足需要,那就可能沒有必要使用適配器。
  • 從表面上看,適配器模式很像門面模式。它們都要對(duì)別的對(duì)象進(jìn)行包裝并改變其呈現(xiàn)的接口。二者的差別在于它們?nèi)绾胃淖兘涌凇iT面元素展現(xiàn)的是一個(gè)簡(jiǎn)化的接口,它并不提供額外的選擇,而且有時(shí)為了方便完成某些常見任務(wù)它還會(huì)做出一些假定。而適配器則要把一個(gè)接口轉(zhuǎn)換為另一個(gè)接口,它并不會(huì)濾除某些能力,也不會(huì)簡(jiǎn)化接口。如果客戶系統(tǒng)期待的API不可用,那就需要用到適配器。
  • 適配器可被實(shí)現(xiàn)為不兼容的方法調(diào)用之間的一個(gè)代碼薄層。
  • 示例:
  • 假如你有一個(gè)對(duì)象還有一個(gè)以三個(gè)字符串為參數(shù)的函數(shù):

      var clientObject = {      string1: 'foo',      string2: 'bar',      string3: 'baz'  };  function interfaceMethod(str1, str2, str3) {  }

    為了把clientObject作為參數(shù)傳遞給interfaceMethod,需要用到適配器。我們可以這樣創(chuàng)建一個(gè):

    function clientToInterfaceAdapter(o) {

      interfaceMethod(o.string1, o.string2, o.string3);

    }
    //現(xiàn)在就可以把整個(gè)對(duì)象傳給這個(gè)函數(shù)
    clientToInterfaceAdapter(clientObject);

clientToInterfaceAdapter函數(shù)的作用就在于對(duì)interfaceMethod函數(shù)進(jìn)行包裝,并把傳遞給它的參數(shù)轉(zhuǎn)換給后者需要的形式。

22.裝飾者模式

裝飾者模式可用來透明地把對(duì)象包裝在具有同樣接口的另一對(duì)象中。這樣一來,你可以給一個(gè)方法添加一些行為,然后將方法調(diào)用傳遞給原始對(duì)象。相對(duì)于創(chuàng)建子類來說,使用裝飾者對(duì)象是一種更靈活的選擇。

23.享元模式

享元模式最適合于解決因創(chuàng)建大量類似對(duì)象而累及的性能問題。這種模式在JavaScript中尤其有用,因?yàn)閺?fù)雜的JavaScript代碼可能很快就會(huì)用光瀏覽器的所有可用內(nèi)存。通過把大量獨(dú)立對(duì)象轉(zhuǎn)化為少量共享對(duì)象,可以降低運(yùn)行Web應(yīng)用程序所需的資源數(shù)量。

享元模式用于減少應(yīng)用程序所需對(duì)象的數(shù)量。這是通過將對(duì)象的內(nèi)部狀態(tài)劃分為內(nèi)在數(shù)據(jù)和外在數(shù)據(jù)兩類而實(shí)現(xiàn)的。內(nèi)在數(shù)據(jù)是指類的內(nèi)部方法所需的信息,沒有這種數(shù)據(jù)的話類不能正常運(yùn)轉(zhuǎn)。外在數(shù)據(jù)則是可以從類身上剝離并存儲(chǔ)在其外部的信息。我們可以將內(nèi)在狀態(tài)相同的所有對(duì)象替換為同一個(gè)共享對(duì)象,這種方法可以把對(duì)象數(shù)量減少到不同內(nèi)在狀態(tài)的數(shù)量。

24.實(shí)現(xiàn)享元模式的一般步驟

  1. 將所有外在數(shù)據(jù)從目標(biāo)剝離。具體做法是盡可能多地刪除該類的屬性,所刪除的應(yīng)該是那種因?qū)嵗惖膶傩?。?gòu)造函數(shù)的參數(shù)也要這樣處理。這些參數(shù)應(yīng)該被添加到該類的各個(gè)方法。這些外在數(shù)據(jù)現(xiàn)在不再保存在類的內(nèi)部,而是由管理器提供給類的方法。經(jīng)過這樣的處理后,目標(biāo)類應(yīng)該依然具有與之前一樣的功能。唯一的區(qū)別在于數(shù)據(jù)的來源發(fā)生了變化。
  2. 創(chuàng)建一個(gè)用來控制該類的實(shí)例化的工廠。這個(gè)工廠應(yīng)該掌握該類所有已創(chuàng)建出來的獨(dú)一無二的實(shí)例。其具體做法之一是用一個(gè)對(duì)象字面量來保存每一個(gè)這類對(duì)象的引用,并以用來生成這些對(duì)象的參數(shù)的唯一性組合作為它們的索引。這樣一來,每次要求工廠提供一個(gè)對(duì)象時(shí),它會(huì)先檢查那個(gè)對(duì)象字面量,看看以前是否請(qǐng)求過這個(gè)對(duì)象。如果是,那么只要返回那個(gè)現(xiàn)有對(duì)象的引用就行。否則它會(huì)創(chuàng)建一個(gè)新對(duì)象并將其引用保存在那個(gè)對(duì)象字面量中,然后返回這個(gè)對(duì)象。另一種做法稱為對(duì)象池,這種技術(shù)用數(shù)組來保存所創(chuàng)建的對(duì)象的引用。它適合于注重可用對(duì)象的數(shù)量而不是那些單獨(dú)配置的實(shí)例的場(chǎng)合。這種技術(shù)可用來將所實(shí)例化的對(duì)象的數(shù)目維持在最低值。工廠會(huì)處理根據(jù)內(nèi)在數(shù)據(jù)創(chuàng)建對(duì)象的所有事宜。
  3. 創(chuàng)建一個(gè)用來保存外在數(shù)據(jù)的管理器。該管理器對(duì)象負(fù)責(zé)控制處理外在數(shù)據(jù)的種種事宜。在實(shí)施優(yōu)化之前,要是需要一個(gè)目標(biāo)類的實(shí)例,你會(huì)把所有數(shù)據(jù)傳給構(gòu)造函數(shù)以創(chuàng)建其新實(shí)例。而現(xiàn)在要是需要一個(gè)實(shí)例,你會(huì)調(diào)用管理器的某個(gè)方法,把所有數(shù)據(jù)都提供給它。這個(gè)方法會(huì)分辨內(nèi)在數(shù)據(jù)和外在數(shù)據(jù)。它把內(nèi)在數(shù)據(jù)提供給工廠對(duì)象以創(chuàng)建一個(gè)對(duì)象(或者,如果已經(jīng)存在這樣一個(gè)對(duì)象的話,則重用該對(duì)象)。外在數(shù)據(jù)則被保存在管理器內(nèi)的一個(gè)數(shù)據(jù)結(jié)構(gòu)中。管理器隨后會(huì)根據(jù)需要將這些數(shù)據(jù)提供給共享對(duì)象的方法,其效果就如同該類有許多實(shí)例一樣。

25.觀察者模式

  • 在事件驅(qū)動(dòng)的環(huán)境中,比如瀏覽器這種持續(xù)尋求用戶關(guān)注的環(huán)境中,觀察者模式(又名發(fā)布者-訂閱者模式)是一種管理人與其任務(wù)之間的關(guān)系(確切的說,是對(duì)象及其行為和狀態(tài)之間的關(guān)系)的得力工具。
  • 觀察者模式中存在兩個(gè)角色:觀察者和被觀察者。

著作權(quán)歸作者所有

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多