欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

學(xué)習(xí)JavaScript設(shè)計(jì)模式(多態(tài))

 更新時(shí)間:2015年11月25日 17:16:11   作者:小平果118  
這篇文章主要帶領(lǐng)大家學(xué)習(xí)JavaScript設(shè)計(jì)模式,其中重點(diǎn)介紹多態(tài),舉例說明多態(tài)的思想,對(duì)多態(tài)進(jìn)行詳細(xì)剖析,感興趣的小伙伴們可以參考一下

多態(tài)的實(shí)際含義是:同一操作作用于不同的對(duì)象上面,可以產(chǎn)生不同的解釋和不同的執(zhí)行結(jié)果。換句話說,給不同的對(duì)象發(fā)送同一個(gè)消息的時(shí)候,這些對(duì)象會(huì)根據(jù)這個(gè)消息分別給出不同的反饋。

從字面上來(lái)理解多態(tài)不太容易,下面我們來(lái)舉例說明一下。

主人家里養(yǎng)了兩只動(dòng)物,分別是一只鴨和一只雞,當(dāng)主人向它們發(fā)出“叫”的命令時(shí),鴨會(huì)“嘎嘎嘎”地叫,而雞會(huì)“咯咯咯”地叫。這兩只動(dòng)物都會(huì)以自己的方式來(lái)發(fā)出叫聲。它們同樣“都是動(dòng)物,并且可以發(fā)出叫聲”,但根據(jù)主人的指令,它們會(huì)各自發(fā)出不同的叫聲。

其實(shí),其中就蘊(yùn)含了多態(tài)的思想。下面我們通過代碼進(jìn)行具體的介紹。

1. 一段“多態(tài)”的JavaScript代碼

我們把上面的故事用JavaScript代碼實(shí)現(xiàn)如下:

var makeSound = function( animal ){
 if ( animal instanceof Duck ){
 console.log( '嘎嘎嘎' );
 }else if ( animal instanceof Chicken ){
 console.log( '咯咯咯' );
 }
};

var Duck = function(){};
var Chicken = function(){};

makeSound( new Duck() ); //嘎嘎嘎
makeSound( new Chicken() ); //咯咯咯 

這段代碼確實(shí)體現(xiàn)了“多態(tài)性”,當(dāng)我們分別向鴨和雞發(fā)出“叫喚”的消息時(shí),它們根據(jù)此消息作出了各自不同的反應(yīng)。但這樣的“多態(tài)性”是無(wú)法令人滿意的,如果后來(lái)又增加了一只動(dòng)物,比如狗,顯然狗的叫聲是“汪汪汪”,此時(shí)我們必須得改動(dòng)makeSound函數(shù),才能讓狗也發(fā)出叫聲。修改代碼總是危險(xiǎn)的,修改的地方越多,程序出錯(cuò)的可能性就越大,而且當(dāng)動(dòng)物的種類越來(lái)越多時(shí),makeSound有可能變成一個(gè)巨大的函數(shù)。

多態(tài)背后的思想是將“做什么”和“誰(shuí)去做以及怎樣去做”分離開來(lái),也就是將“不變的事物”與 “可能改變的事物”分離開來(lái)。在這個(gè)故事中,動(dòng)物都會(huì)叫,這是不變的,但是不同類型的動(dòng)物具體怎么叫是可變的。把不變的部分隔離出來(lái),把可變的部分封裝起來(lái),這給予了我們擴(kuò)展程序的能力,程序看起來(lái)是可生長(zhǎng)的,也是符合開放-封閉原則的,相對(duì)于修改代碼來(lái)說,僅僅增加代碼就能完成同樣的功能,這顯然優(yōu)雅和安全得多。

2. 對(duì)象的多態(tài)性

下面是改寫后的代碼,首先我們把不變的部分隔離出來(lái),那就是所有的動(dòng)物都會(huì)發(fā)出叫聲:

var makeSound = function( animal ){
 animal.sound();
};

然后把可變的部分各自封裝起來(lái),我們剛才談到的多態(tài)性實(shí)際上指的是對(duì)象的多態(tài)性:

var Duck = function(){} 

Duck.prototype.sound = function(){
 console.log( '嘎嘎嘎' );
};

var Chicken = function(){}

Chicken.prototype.sound = function(){
 console.log( '咯咯咯' );
};

makeSound( new Duck() ); //嘎嘎嘎
makeSound( new Chicken() ); //咯咯咯

現(xiàn)在我們向鴨和雞都發(fā)出“叫喚”的消息,它們接到消息后分別作出了不同的反應(yīng)。如果有一天動(dòng)物世界里又增加了一只狗,這時(shí)候只要簡(jiǎn)單地追加一些代碼就可以了,而不用改動(dòng)以前的makeSound函數(shù),如下所示:

var Dog = function(){}

Dog.prototype.sound = function(){
 console.log( '汪汪汪' );
};

makeSound( new Dog() ); //汪汪汪

3. 類型檢查和多態(tài)

類型檢查是在表現(xiàn)出對(duì)象多態(tài)性之前的一個(gè)繞不開的話題,但JavaScript是一門不必進(jìn)行類型檢查的動(dòng)態(tài)類型語(yǔ)言,為了真正了解多態(tài)的目的,我們需要轉(zhuǎn)一個(gè)彎,從一門靜態(tài)類型語(yǔ)言說起。

靜態(tài)類型語(yǔ)言在編譯時(shí)會(huì)進(jìn)行類型匹配檢查。以Java為例,由于在代碼編譯時(shí)要進(jìn)行嚴(yán)格的類型檢查,所以不能給變量賦予不同類型的值,這種類型檢查有時(shí)候會(huì)讓代碼顯得僵硬,代碼如下:

String str;

str = abc; //沒有問題 
str = 2; //報(bào)錯(cuò)

現(xiàn)在我們嘗試把上面讓鴨子和雞叫喚的例子換成Java代碼:

public class Duck { //鴨子類
 public void makeSound(){
 System.out.println( 嘎嘎嘎 );
 }
}

public class Chicken { //雞類
 public void makeSound(){
 System.out.println( 咯咯咯 );
 }
}


public class AnimalSound {
 public void makeSound( Duck duck ){ //(1)
 duck.makeSound();
 }

}

public class Test {
 public static void main( String args[] ){
 AnimalSound animalSound = new AnimalSound();
 Duck duck = new Duck();
 animalSound.makeSound( duck ); //輸出:嘎嘎嘎
 }
}

我們已經(jīng)順利地讓鴨子可以發(fā)出叫聲,但如果現(xiàn)在想讓雞也叫喚起來(lái),我們發(fā)現(xiàn)這是一件不可能實(shí)現(xiàn)的事情。因?yàn)?1)處AnimalSound類的makeSound方法,被我們規(guī)定為只能接受Duck類型的參數(shù):

public class Test {
 public static void main( String args[] ){
 AnimalSound animalSound = new AnimalSound();
 Chicken chicken = new Chicken();
 animalSound.makeSound( chicken ); //報(bào)錯(cuò),只能接受Duck類型的參數(shù)
 }
} 

某些時(shí)候,在享受靜態(tài)語(yǔ)言類型檢查帶來(lái)的安全性的同時(shí),我們亦會(huì)感覺被束縛住了手腳。

為了解決這一問題,靜態(tài)類型的面向?qū)ο笳Z(yǔ)言通常被設(shè)計(jì)為可以向上轉(zhuǎn)型:當(dāng)給一個(gè)類變量賦值時(shí),這個(gè)變量的類型既可以使用這個(gè)類本身,也可以使用這個(gè)類的超類。這就像我們?cè)诿枋鎏焐系囊恢宦槿富蛘咭恢幌铲o時(shí),通常說“一只麻雀在飛”或者“一只喜鵲在飛”。但如果想忽略它們的具體類型,那么也可以說”一只鳥在飛“。

同理,當(dāng)Duck對(duì)象和Chicken對(duì)象的類型都被隱藏在超類型Animal身后,Duck對(duì)象和Chicken對(duì)象就能被交換使用,這是讓對(duì)象表現(xiàn)出多態(tài)性的必經(jīng)之路,而多態(tài)性的表現(xiàn)正是實(shí)現(xiàn)眾多設(shè)計(jì)模式的目標(biāo)。

4. 使用繼承得到多態(tài)效果

使用繼承來(lái)得到多態(tài)效果,是讓對(duì)象表現(xiàn)出多態(tài)性的最常用手段。繼承通常包括實(shí)現(xiàn)繼承和接口繼承。本節(jié)我們討論實(shí)現(xiàn)繼承,接口繼承的例子請(qǐng)參見第21章。

我們先創(chuàng)建一個(gè)Animal抽象類,再分別讓Duck和Chicken都繼承自Animal抽象類,下述代碼中(1)處和(2)處的賦值語(yǔ)句顯然是成立的,因?yàn)轼喿雍碗u也是動(dòng)物:

public abstract class Animal {
 abstract void makeSound(); //抽象方法
} 

public class Chicken extends Animal{
 public void makeSound(){
 System.out.println( 咯咯咯 );
 }
}

public class Duck extends Animal{
 public void makeSound(){
 System.out.println( 嘎嘎嘎 );
 }
}

Animal duck = new Duck(); //(1)
Animal chicken = new Chicken(); //(2)

現(xiàn)在剩下的就是讓AnimalSound類的makeSound方法接受Animal類型的參數(shù),而不是具體的Duck類型或者Chicken類型:

public class AnimalSound{
 public void makeSound( Animal animal ){ //接受Animal類型的參數(shù)
 animal.makeSound();
 }
}

public class Test {
 public static void main( String args[] ){
 AnimalSound animalSound= new AnimalSound ();
 Animal duck = new Duck();
 Animal chicken = new Chicken();
 animalSound.makeSound( duck ); //輸出嘎嘎嘎
 animalSound.makeSound( chicken ); //輸出咯咯咯
 }
}

5. JavaScript的多態(tài)

從前面的講解我們得知,多態(tài)的思想實(shí)際上是把“做什么”和“誰(shuí)去做”分離開來(lái),要實(shí)現(xiàn)這一點(diǎn),歸根結(jié)底先要消除類型之間的耦合關(guān)系。如果類型之間的耦合關(guān)系沒有被消除,那么我們?cè)趍akeSound方法中指定了發(fā)出叫聲的對(duì)象是某個(gè)類型,它就不可能再被替換為另外一個(gè)類型。在Java中,可以通過向上轉(zhuǎn)型來(lái)實(shí)現(xiàn)多態(tài)。

而JavaScript的變量類型在運(yùn)行期是可變的。一個(gè)JavaScript對(duì)象,既可以表示Duck類型的對(duì)象,又可以表示Chicken類型的對(duì)象,這意味著JavaScript對(duì)象的多態(tài)性是與生俱來(lái)的。

這種與生俱來(lái)的多態(tài)性并不難解釋。JavaScript作為一門動(dòng)態(tài)類型語(yǔ)言,它在編譯時(shí)沒有類型檢查的過程,既沒有檢查創(chuàng)建的對(duì)象類型,又沒有檢查傳遞的參數(shù)類型。在2節(jié)的代碼示例中,我們既可以往makeSound函數(shù)里傳遞duck對(duì)象當(dāng)作參數(shù),也可以傳遞chicken對(duì)象當(dāng)作參數(shù)。

由此可見,某一種動(dòng)物能否發(fā)出叫聲,只取決于它有沒有makeSound方法,而不取決于它是否是某種類型的對(duì)象,這里不存在任何程度上的“類型耦合”。這正是我們從上一節(jié)的鴨子類型中領(lǐng)悟的道理。在JavaScript中,并不需要諸如向上轉(zhuǎn)型之類的技術(shù)來(lái)取得多態(tài)的效果。

6. 多態(tài)在面向?qū)ο蟪绦蛟O(shè)計(jì)中的作用

有許多人認(rèn)為,多態(tài)是面向?qū)ο缶幊陶Z(yǔ)言中最重要的技術(shù)。但我們目前還很難看出這一點(diǎn),畢竟大部分人都不關(guān)心雞是怎么叫的,也不想知道鴨是怎么叫的。讓雞和鴨在同一個(gè)消息之下發(fā)出不同的叫聲,這跟程序員有什么關(guān)系呢?

Martin Fowler在《重構(gòu):改善既有代碼的設(shè)計(jì)》里寫到:

多態(tài)的最根本好處在于,你不必再向?qū)ο笤儐枴澳闶鞘裁搭愋汀倍蟾鶕?jù)得到的答案調(diào)用對(duì)象的某個(gè)行為——你只管調(diào)用該行為就是了,其他的一切多態(tài)機(jī)制都會(huì)為你安排妥當(dāng)。

換句話說,多態(tài)最根本的作用就是通過把過程化的條件分支語(yǔ)句轉(zhuǎn)化為對(duì)象的多態(tài)性,從而消除這些條件分支語(yǔ)句。

Martin Fowler的話可以用下面這個(gè)例子很好地詮釋:

在電影的拍攝現(xiàn)場(chǎng),當(dāng)導(dǎo)演喊出“action”時(shí),主角開始背臺(tái)詞,照明師負(fù)責(zé)打燈光,后面的群眾演員假裝中槍倒地,道具師往鏡頭里撒上雪花。在得到同一個(gè)消息時(shí),每個(gè)對(duì)象都知道自己應(yīng)該做什么。如果不利用對(duì)象的多態(tài)性,而是用面向過程的方式來(lái)編寫這一段代碼,那么相當(dāng)于在電影開始拍攝之后,導(dǎo)演每次都要走到每個(gè)人的面前,確認(rèn)它們的職業(yè)分工(類型),然后告訴他們要做什么。如果映射到程序中,那么程序中將充斥著條件分支語(yǔ)句。

利用對(duì)象的多態(tài)性,導(dǎo)演在發(fā)布消息時(shí),就不必考慮各個(gè)對(duì)象接到消息后應(yīng)該做什么。對(duì)象應(yīng)該做什么并不是臨時(shí)決定的,而是已經(jīng)事先約定和排練完畢的。每個(gè)對(duì)象應(yīng)該做什么,已經(jīng)成為了該對(duì)象的一個(gè)方法,被安裝在對(duì)象的內(nèi)部,每個(gè)對(duì)象負(fù)責(zé)它們自己的行為。所以這些對(duì)象可以根據(jù)同一個(gè)消息,有條不紊地分別進(jìn)行各自的工作。

將行為分布在各個(gè)對(duì)象中,并讓這些對(duì)象各自負(fù)責(zé)自己的行為,這正是面向?qū)ο笤O(shè)計(jì)的優(yōu)點(diǎn)。

再看一個(gè)現(xiàn)實(shí)開發(fā)中遇到的例子,這個(gè)例子的思想和動(dòng)物叫聲的故事非常相似。

假設(shè)我們要編寫一個(gè)地圖應(yīng)用,現(xiàn)在有兩家可選的地圖API提供商供我們接入自己的應(yīng)用。目前我們選擇的是谷歌地圖,谷歌地圖的API中提供了show方法,負(fù)責(zé)在頁(yè)面上展示整個(gè)地圖。示例代碼如下:

var googleMap = {
 show: function(){
 console.log( '開始渲染google地圖' );
 }
};

var renderMap = function(){
 googleMap.show(); 
};

renderMap(); // 輸出: 開始渲染google地圖 

后來(lái)因?yàn)槟承┰?,要把谷歌地圖換成百度地圖,為了讓renderMap函數(shù)保持一定的彈性,我們用一些條件分支來(lái)讓renderMap函數(shù)同時(shí)支持谷歌地圖和百度地圖:

var googleMap = {
 show: function(){
 console.log( '開始渲染google地圖' );
 }
};

var baiduMap = {
 show: function(){
 console.log( '開始渲染baidu地圖' );
 }
};

var renderMap = function( type ){
 if ( type === 'google' ){
 googleMap.show(); 
 }else if ( type === 'baidu' ){
 baiduMap.show();
 }
};

renderMap( 'google' ); // 輸出: 開始渲染google地圖 
renderMap( 'baidu' ); // 輸出: 開始渲染baidu地圖 

可以看到,雖然renderMap函數(shù)目前保持了一定的彈性,但這種彈性是很脆弱的,一旦需要替換成搜搜地圖,那無(wú)疑必須得改動(dòng)renderMap函數(shù),繼續(xù)往里面堆砌條件分支語(yǔ)句。

我們還是先把程序中相同的部分抽象出來(lái),那就是顯示某個(gè)地圖:

var renderMap = function( map ){
 if ( map.show instanceof Function ){
 map.show();
 }
};

renderMap( googleMap ); // 輸出: 開始渲染google地圖 
renderMap( baiduMap ); // 輸出: 開始渲染baidu地圖 

現(xiàn)在來(lái)找找這段代碼中的多態(tài)性。當(dāng)我們向谷歌地圖對(duì)象和百度地圖對(duì)象分別發(fā)出“展示地圖”的消息時(shí),會(huì)分別調(diào)用它們的show方法,就會(huì)產(chǎn)生各自不同的執(zhí)行結(jié)果。對(duì)象的多態(tài)性提示我們,“做什么”和“怎么去做”是可以分開的,即使以后增加了搜搜地圖,renderMap函數(shù)仍然不需要做任何改變,如下所示:

var sosoMap = {
 show: function(){
 console.log( '開始渲染soso地圖' );
 }
};

renderMap( sosoMap ); // 輸出: 開始渲染soso地圖 

在這個(gè)例子中,我們假設(shè)每個(gè)地圖API提供展示地圖的方法名都是show,在實(shí)際開發(fā)中也許不會(huì)如此順利,這時(shí)候可以借助適配器模式來(lái)解決問題。

以上就是本文的全部?jī)?nèi)容,很全面,以生動(dòng)的舉例來(lái)幫助大家學(xué)習(xí)多態(tài),希望大家能夠真正的有所收獲。

相關(guān)文章

  • JS跨域請(qǐng)求外部服務(wù)器的資源

    JS跨域請(qǐng)求外部服務(wù)器的資源

    這篇文章主要介紹了JS跨域請(qǐng)求外部服務(wù)器的資源,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友參考下吧
    2017-02-02
  • javascript HTML5文件上傳FileReader API

    javascript HTML5文件上傳FileReader API

    這篇文章主要介紹了javascript HTML5文件上傳FileReader API的相關(guān)資料,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-04-04
  • 如何通過setTimeout理解JS運(yùn)行機(jī)制詳解

    如何通過setTimeout理解JS運(yùn)行機(jī)制詳解

    這篇文章主要給大家介紹了關(guān)于如何通過setTimeout理解JS運(yùn)行機(jī)制的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用js具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-03-03
  • javascript forEach函數(shù)實(shí)現(xiàn)代碼

    javascript forEach函數(shù)實(shí)現(xiàn)代碼

    在Base2中找到一個(gè)叫forEach的函數(shù),是我見過的最好的實(shí)現(xiàn)。挖出來(lái)分析一下。它能對(duì)各種普通對(duì)象,字符串,數(shù)組以及類數(shù)組進(jìn)行遍歷。如果原游覽器的對(duì)象已實(shí)現(xiàn)此函數(shù),它則調(diào)用原對(duì)象的函數(shù)。
    2010-01-01
  • 用javascript實(shí)現(xiàn)簡(jiǎn)單計(jì)算器

    用javascript實(shí)現(xiàn)簡(jiǎn)單計(jì)算器

    這篇文章主要為大家詳細(xì)介紹了用javascript實(shí)現(xiàn)簡(jiǎn)單計(jì)算器,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-08-08
  • JS實(shí)現(xiàn)合并兩個(gè)數(shù)組并去除重復(fù)項(xiàng)只留一個(gè)的方法

    JS實(shí)現(xiàn)合并兩個(gè)數(shù)組并去除重復(fù)項(xiàng)只留一個(gè)的方法

    這篇文章主要介紹了JS實(shí)現(xiàn)合并兩個(gè)數(shù)組并去除重復(fù)項(xiàng)只留一個(gè)的方法,涉及JavaScript數(shù)組合并及去重的相關(guān)技巧,需要的朋友可以參考下
    2015-12-12
  • javascript抽象工廠模式詳細(xì)說明

    javascript抽象工廠模式詳細(xì)說明

    這篇文章主要介紹了javascript抽象工廠模式詳細(xì)說明,需要的朋友可以參考下
    2014-12-12
  • js保留兩位小數(shù)最簡(jiǎn)單的實(shí)現(xiàn)方法

    js保留兩位小數(shù)最簡(jiǎn)單的實(shí)現(xiàn)方法

    JS數(shù)據(jù)格式化是在進(jìn)行web前端開發(fā)時(shí)常碰到的事情,特別是在數(shù)據(jù)類型為Float的數(shù)據(jù)就需要特殊處理,如保留兩位小數(shù)、小數(shù)點(diǎn)后的數(shù)據(jù)是否需要四舍五入等等,下面這篇文章主要給大家介紹了關(guān)于js保留兩位小數(shù)最簡(jiǎn)單的實(shí)現(xiàn)方法,需要的朋友可以參考下
    2023-05-05
  • JS判斷用戶用的哪個(gè)瀏覽器實(shí)例詳解

    JS判斷用戶用的哪個(gè)瀏覽器實(shí)例詳解

    這篇文章主要介紹了JS判斷用戶用的哪個(gè)瀏覽器的實(shí)例代碼,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2018-10-10
  • JS簡(jiǎn)單獲取當(dāng)前日期時(shí)間的方法(如:2017-03-29 11:41:10 星期四)

    JS簡(jiǎn)單獲取當(dāng)前日期時(shí)間的方法(如:2017-03-29 11:41:10 星期四)

    這篇文章主要介紹了JS簡(jiǎn)單獲取當(dāng)前日期時(shí)間的方法,涉及javascript針對(duì)當(dāng)前日期時(shí)間的簡(jiǎn)單運(yùn)算操作,需要的朋友可以參考下
    2017-03-03

最新評(píng)論