亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > JavaScript > 正文

學習JavaScript設計模式(多態)

2019-11-20 11:09:46
字體:
來源:轉載
供稿:網友

多態的實際含義是:同一操作作用于不同的對象上面,可以產生不同的解釋和不同的執行結果。換句話說,給不同的對象發送同一個消息的時候,這些對象會根據這個消息分別給出不同的反饋。

從字面上來理解多態不太容易,下面我們來舉例說明一下。

主人家里養了兩只動物,分別是一只鴨和一只雞,當主人向它們發出“叫”的命令時,鴨會“嘎嘎嘎”地叫,而雞會“咯咯咯”地叫。這兩只動物都會以自己的方式來發出叫聲。它們同樣“都是動物,并且可以發出叫聲”,但根據主人的指令,它們會各自發出不同的叫聲。

其實,其中就蘊含了多態的思想。下面我們通過代碼進行具體的介紹。

1. 一段“多態”的JavaScript代碼

我們把上面的故事用JavaScript代碼實現如下:

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() ); //咯咯咯 

這段代碼確實體現了“多態性”,當我們分別向鴨和雞發出“叫喚”的消息時,它們根據此消息作出了各自不同的反應。但這樣的“多態性”是無法令人滿意的,如果后來又增加了一只動物,比如狗,顯然狗的叫聲是“汪汪汪”,此時我們必須得改動makeSound函數,才能讓狗也發出叫聲。修改代碼總是危險的,修改的地方越多,程序出錯的可能性就越大,而且當動物的種類越來越多時,makeSound有可能變成一個巨大的函數。

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

2. 對象的多態性

下面是改寫后的代碼,首先我們把不變的部分隔離出來,那就是所有的動物都會發出叫聲:

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

然后把可變的部分各自封裝起來,我們剛才談到的多態性實際上指的是對象的多態性:

var Duck = function(){} Duck.prototype.sound = function(){ console.log( '嘎嘎嘎' );};var Chicken = function(){}Chicken.prototype.sound = function(){ console.log( '咯咯咯' );};makeSound( new Duck() ); //嘎嘎嘎makeSound( new Chicken() ); //咯咯咯

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

var Dog = function(){}Dog.prototype.sound = function(){ console.log( '汪汪汪' );};makeSound( new Dog() ); //汪汪汪

3. 類型檢查和多態

類型檢查是在表現出對象多態性之前的一個繞不開的話題,但JavaScript是一門不必進行類型檢查的動態類型語言,為了真正了解多態的目的,我們需要轉一個彎,從一門靜態類型語言說起。

靜態類型語言在編譯時會進行類型匹配檢查。以Java為例,由于在代碼編譯時要進行嚴格的類型檢查,所以不能給變量賦予不同類型的值,這種類型檢查有時候會讓代碼顯得僵硬,代碼如下:

String str;str = abc; //沒有問題 str = 2; //報錯

現在我們嘗試把上面讓鴨子和雞叫喚的例子換成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 ); //輸出:嘎嘎嘎 }}

我們已經順利地讓鴨子可以發出叫聲,但如果現在想讓雞也叫喚起來,我們發現這是一件不可能實現的事情。因為(1)處AnimalSound類的makeSound方法,被我們規定為只能接受Duck類型的參數:

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

某些時候,在享受靜態語言類型檢查帶來的安全性的同時,我們亦會感覺被束縛住了手腳。

為了解決這一問題,靜態類型的面向對象語言通常被設計為可以向上轉型:當給一個類變量賦值時,這個變量的類型既可以使用這個類本身,也可以使用這個類的超類。這就像我們在描述天上的一只麻雀或者一只喜鵲時,通常說“一只麻雀在飛”或者“一只喜鵲在飛”。但如果想忽略它們的具體類型,那么也可以說”一只鳥在飛“。

同理,當Duck對象和Chicken對象的類型都被隱藏在超類型Animal身后,Duck對象和Chicken對象就能被交換使用,這是讓對象表現出多態性的必經之路,而多態性的表現正是實現眾多設計模式的目標。

4. 使用繼承得到多態效果

使用繼承來得到多態效果,是讓對象表現出多態性的最常用手段。繼承通常包括實現繼承和接口繼承。本節我們討論實現繼承,接口繼承的例子請參見第21章。

我們先創建一個Animal抽象類,再分別讓Duck和Chicken都繼承自Animal抽象類,下述代碼中(1)處和(2)處的賦值語句顯然是成立的,因為鴨子和雞也是動物:

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)

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

public class AnimalSound{ public void makeSound( Animal animal ){ //接受Animal類型的參數 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的多態

從前面的講解我們得知,多態的思想實際上是把“做什么”和“誰去做”分離開來,要實現這一點,歸根結底先要消除類型之間的耦合關系。如果類型之間的耦合關系沒有被消除,那么我們在makeSound方法中指定了發出叫聲的對象是某個類型,它就不可能再被替換為另外一個類型。在Java中,可以通過向上轉型來實現多態。

而JavaScript的變量類型在運行期是可變的。一個JavaScript對象,既可以表示Duck類型的對象,又可以表示Chicken類型的對象,這意味著JavaScript對象的多態性是與生俱來的。

這種與生俱來的多態性并不難解釋。JavaScript作為一門動態類型語言,它在編譯時沒有類型檢查的過程,既沒有檢查創建的對象類型,又沒有檢查傳遞的參數類型。在2節的代碼示例中,我們既可以往makeSound函數里傳遞duck對象當作參數,也可以傳遞chicken對象當作參數。

由此可見,某一種動物能否發出叫聲,只取決于它有沒有makeSound方法,而不取決于它是否是某種類型的對象,這里不存在任何程度上的“類型耦合”。這正是我們從上一節的鴨子類型中領悟的道理。在JavaScript中,并不需要諸如向上轉型之類的技術來取得多態的效果。

6. 多態在面向對象程序設計中的作用

有許多人認為,多態是面向對象編程語言中最重要的技術。但我們目前還很難看出這一點,畢竟大部分人都不關心雞是怎么叫的,也不想知道鴨是怎么叫的。讓雞和鴨在同一個消息之下發出不同的叫聲,這跟程序員有什么關系呢?

Martin Fowler在《重構:改善既有代碼的設計》里寫到:

多態的最根本好處在于,你不必再向對象詢問“你是什么類型”而后根據得到的答案調用對象的某個行為――你只管調用該行為就是了,其他的一切多態機制都會為你安排妥當。

換句話說,多態最根本的作用就是通過把過程化的條件分支語句轉化為對象的多態性,從而消除這些條件分支語句。

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

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

利用對象的多態性,導演在發布消息時,就不必考慮各個對象接到消息后應該做什么。對象應該做什么并不是臨時決定的,而是已經事先約定和排練完畢的。每個對象應該做什么,已經成為了該對象的一個方法,被安裝在對象的內部,每個對象負責它們自己的行為。所以這些對象可以根據同一個消息,有條不紊地分別進行各自的工作。

將行為分布在各個對象中,并讓這些對象各自負責自己的行為,這正是面向對象設計的優點。

再看一個現實開發中遇到的例子,這個例子的思想和動物叫聲的故事非常相似。

假設我們要編寫一個地圖應用,現在有兩家可選的地圖API提供商供我們接入自己的應用。目前我們選擇的是谷歌地圖,谷歌地圖的API中提供了show方法,負責在頁面上展示整個地圖。示例代碼如下:

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

后來因為某些原因,要把谷歌地圖換成百度地圖,為了讓renderMap函數保持一定的彈性,我們用一些條件分支來讓renderMap函數同時支持谷歌地圖和百度地圖:

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函數目前保持了一定的彈性,但這種彈性是很脆弱的,一旦需要替換成搜搜地圖,那無疑必須得改動renderMap函數,繼續往里面堆砌條件分支語句。

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

var renderMap = function( map ){ if ( map.show instanceof Function ){ map.show(); }};renderMap( googleMap ); // 輸出: 開始渲染google地圖 renderMap( baiduMap ); // 輸出: 開始渲染baidu地圖 

現在來找找這段代碼中的多態性。當我們向谷歌地圖對象和百度地圖對象分別發出“展示地圖”的消息時,會分別調用它們的show方法,就會產生各自不同的執行結果。對象的多態性提示我們,“做什么”和“怎么去做”是可以分開的,即使以后增加了搜搜地圖,renderMap函數仍然不需要做任何改變,如下所示:

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

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

以上就是本文的全部內容,很全面,以生動的舉例來幫助大家學習多態,希望大家能夠真正的有所收獲。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
国产精品视频一区国模私拍| 狠狠干狠狠久久| 国产在线拍偷自揄拍精品| 久久五月情影视| 国产日韩欧美影视| 午夜精品福利电影| 日韩麻豆第一页| 亚洲天堂成人在线视频| 欧美最猛性xxxxx(亚洲精品)| 国产视频在线一区二区| 日韩精品一区二区三区第95| 亚洲视频在线观看网站| 色噜噜狠狠色综合网图区| 欧美日韩电影在线观看| 国产日韩在线观看av| 国产精品ⅴa在线观看h| 97人洗澡人人免费公开视频碰碰碰| 亚洲综合中文字幕在线观看| 精品国产乱码久久久久久虫虫漫画| 亚洲精品国产电影| 欧美电影免费观看大全| 久久精品国产亚洲7777| 国产91精品黑色丝袜高跟鞋| 尤物99国产成人精品视频| 亚洲视频电影图片偷拍一区| 欧美精品videossex性护士| 久久久久久久久久婷婷| 日韩中文字幕在线| 日韩电影网在线| 精品少妇一区二区30p| 国产激情久久久久| 久久在线视频在线| 国产一区二区三区在线观看视频| 国产精品视频xxxx| 国产成人亚洲精品| 亚洲欧美精品中文字幕在线| 欧美性xxxx极品高清hd直播| 91亚洲精品久久久久久久久久久久| 久久精品视频一| 国产剧情日韩欧美| 国产精品免费一区二区三区都可以| 日韩av观看网址| 精品国内亚洲在观看18黄| 欧美大片大片在线播放| 亚洲欧美日韩一区二区在线| 欧美精品在线第一页| 色偷偷亚洲男人天堂| 欧美激情成人在线视频| 亚洲精品在线视频| 亚洲精品成人久久久| 日韩一二三在线视频播| 美日韩精品免费视频| 97人人做人人爱| 国产噜噜噜噜噜久久久久久久久| 久久99视频精品| 高清欧美性猛交| 日韩电影免费在线观看中文字幕| 成人情趣片在线观看免费| 日韩精品极品视频| 亚洲人成77777在线观看网| 日本久久久久久| 国产精品第一视频| 操日韩av在线电影| 最近的2019中文字幕免费一页| 亚洲最大中文字幕| 日本精品视频在线播放| 亚洲激情在线视频| 欧美丰满少妇xxxxx做受| 亚洲激情视频在线| 日韩欧美有码在线| 日韩视频免费观看| 亚洲片在线观看| 国产成人黄色av| 热门国产精品亚洲第一区在线| 久久久久这里只有精品| 国产精品香蕉国产| 亚洲国产精品久久精品怡红院| 国产成人久久久精品一区| 亚洲美女久久久| 91美女片黄在线观| 日韩在线观看你懂的| 亚洲欧美精品一区二区| 国产精品美女久久| 国产精品自产拍高潮在线观看| 成人性教育视频在线观看| 欧美日韩国产二区| 中文字幕日韩高清| 欧美影院久久久| 国内偷自视频区视频综合| 久久久亚洲天堂| 国产欧美精品va在线观看| 久久久久久久久久久av| 午夜精品久久久久久久久久久久久| 欧美视频国产精品| 精品国产一区二区三区久久狼5月| 亚洲欧美在线免费观看| 国产精品久久91| 日韩最新中文字幕电影免费看| 国产va免费精品高清在线| 91sao在线观看国产| 国产欧美日韩中文字幕在线| 国产精品电影久久久久电影网| 欧美巨乳在线观看| 欧美最近摘花xxxx摘花| 欧美多人爱爱视频网站| 少妇精69xxtheporn| 美女视频久久黄| 国产欧美精品xxxx另类| 日日噜噜噜夜夜爽亚洲精品| 一区二区成人精品| 欧美日韩精品在线| 国产精品嫩草视频| 日韩av网站电影| 欧美人成在线视频| 色狠狠久久aa北条麻妃| 久久久亚洲欧洲日产国码aⅴ| 91精品视频免费观看| 久久综合伊人77777| 国产一区视频在线| 成人乱人伦精品视频在线观看| 精品少妇一区二区30p| 日本精品一区二区三区在线播放视频| 亚洲精品国产拍免费91在线| 国外成人免费在线播放| 欧美激情乱人伦| 欧美wwwxxxx| 欧美电影免费在线观看| 日韩美女写真福利在线观看| 中文字幕亚洲综合久久筱田步美| 欧美成人sm免费视频| 91精品久久久久久久久久久久久| 午夜精品福利在线观看| 中文国产亚洲喷潮| 国产ts人妖一区二区三区| 精品日本高清在线播放| 欧美精品电影免费在线观看| 欧美性精品220| 亚洲欧洲一区二区三区久久| 久久亚洲影音av资源网| 日韩中文字幕欧美| 2018日韩中文字幕| 国产日韩专区在线| 国产精品美女久久久久av超清| 欧美日韩一区二区三区| 久久久免费观看视频| 性色av一区二区三区免费| 亚洲精品视频播放| 色yeye香蕉凹凸一区二区av| 51色欧美片视频在线观看| 亚洲午夜性刺激影院| 国产性猛交xxxx免费看久久| 亚洲国产女人aaa毛片在线| 国产精品igao视频| 不用播放器成人网| 欧美性一区二区三区| 亚洲国产成人在线视频| 成人欧美一区二区三区黑人孕妇| 欧美性少妇18aaaa视频| 国产黑人绿帽在线第一区| 国产日产欧美精品| 91av视频在线免费观看| 国产精品网红直播| 亚洲色图国产精品| 国产伦精品免费视频|