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

首頁 > 語言 > JavaScript > 正文

學習JavaScript設計模式(多態)

2024-05-06 16:25:16
字體:
來源:轉載
供稿:網友

這篇文章主要帶領大家學習JavaScript設計模式,其中重點介紹多態,舉例說明多態的思想,對多態進行詳細剖析,感興趣的小伙伴們可以參考一下

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

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

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

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

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

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

 

 
  1. var makeSound = function( animal ){ 
  2. if ( animal instanceof Duck ){ 
  3. console.log( '嘎嘎嘎' ); 
  4. }else if ( animal instanceof Chicken ){ 
  5. console.log( '咯咯咯' ); 
  6. }; 
  7.  
  8. var Duck = function(){}; 
  9. var Chicken = function(){}; 
  10.  
  11. makeSound( new Duck() ); //嘎嘎嘎 
  12. makeSound( new Chicken() ); //咯咯咯  

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

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

2. 對象的多態性

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

 

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

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

 

 
  1. var Duck = function(){}  
  2.  
  3. Duck.prototype.sound = function(){ 
  4. console.log( '嘎嘎嘎' ); 
  5. }; 
  6.  
  7. var Chicken = function(){} 
  8.  
  9. Chicken.prototype.sound = function(){ 
  10. console.log( '咯咯咯' ); 
  11. }; 
  12.  
  13. makeSound( new Duck() ); //嘎嘎嘎 
  14. makeSound( new Chicken() ); //咯咯咯 

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

 

 
  1. var Dog = function(){} 
  2.  
  3. Dog.prototype.sound = function(){ 
  4. console.log( '汪汪汪' ); 
  5. }; 
  6.  
  7. makeSound( new Dog() ); //汪汪汪 

3. 類型檢查和多態

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

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

 

 
  1. String str; 
  2.  
  3. str = abc; //沒有問題  
  4. str = 2; //報錯 

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

 

 
  1. public class Duck { //鴨子類 
  2. public void makeSound(){ 
  3. System.out.println( 嘎嘎嘎 ); 
  4.  
  5. public class Chicken { //雞類 
  6. public void makeSound(){ 
  7. System.out.println( 咯咯咯 ); 
  8.  
  9.  
  10. public class AnimalSound { 
  11. public void makeSound( Duck duck ){ //(1) 
  12. duck.makeSound(); 
  13.  
  14.  
  15. public class Test { 
  16. public static void main( String args[] ){ 
  17. AnimalSound animalSound = new AnimalSound(); 
  18. Duck duck = new Duck(); 
  19. animalSound.makeSound( duck ); //輸出:嘎嘎嘎 

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

 

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

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

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

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

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

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

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

 

 
  1. public abstract class Animal { 
  2. abstract void makeSound(); //抽象方法 
  3. }  
  4.  
  5. public class Chicken extends Animal{ 
  6. public void makeSound(){ 
  7. System.out.println( 咯咯咯 ); 
  8.  
  9. public class Duck extends Animal{ 
  10. public void makeSound(){ 
  11. System.out.println( 嘎嘎嘎 ); 
  12.  
  13. Animal duck = new Duck(); //(1) 
  14. Animal chicken = new Chicken(); //(2) 

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

 

 
  1. public class AnimalSound{ 
  2. public void makeSound( Animal animal ){ //接受Animal類型的參數 
  3. animal.makeSound(); 
  4.  
  5. public class Test { 
  6. public static void main( String args[] ){ 
  7. AnimalSound animalSound= new AnimalSound (); 
  8. Animal duck = new Duck(); 
  9. Animal chicken = new Chicken(); 
  10. animalSound.makeSound( duck ); //輸出嘎嘎嘎 
  11. 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方法,負責在頁面上展示整個地圖。示例代碼如下:

 

 
  1. var googleMap = { 
  2. show: function(){ 
  3. console.log( '開始渲染google地圖' ); 
  4. }; 
  5.  
  6. var renderMap = function(){ 
  7. googleMap.show();  
  8. }; 
  9.  
  10. renderMap(); // 輸出: 開始渲染google地圖  

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

 

 
  1. var googleMap = { 
  2. show: function(){ 
  3. console.log( '開始渲染google地圖' ); 
  4. }; 
  5.  
  6. var baiduMap = { 
  7. show: function(){ 
  8. console.log( '開始渲染baidu地圖' ); 
  9. }; 
  10.  
  11. var renderMap = function( type ){ 
  12. if ( type === 'google' ){ 
  13. googleMap.show();  
  14. }else if ( type === 'baidu' ){ 
  15. baiduMap.show(); 
  16. }; 
  17.  
  18. renderMap( 'google' ); // 輸出: 開始渲染google地圖  
  19. renderMap( 'baidu' ); // 輸出: 開始渲染baidu地圖  

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

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

 

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

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

 

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

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

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


注:相關教程知識閱讀請移步到JavaScript/Ajax教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表

圖片精選

亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
亚洲精品wwww| 国产女人精品视频| 亚洲a∨日韩av高清在线观看| 欧美激情视频播放| 色综合色综合久久综合频道88| 精品女同一区二区三区在线播放| 91久久精品国产91性色| 精品少妇一区二区30p| 国产精品视频免费在线观看| 国产精品激情av在线播放| 日韩精品在线免费| 91精品国产乱码久久久久久久久| 久久久久久久一| 欧美性一区二区三区| 黑人巨大精品欧美一区二区免费| 91成人在线视频| 91久久久久久久一区二区| 夜夜嗨av色综合久久久综合网| 欧美大码xxxx| 亚洲第一天堂av| 亚洲一品av免费观看| 亚洲精品成a人在线观看| 欧美日韩中文字幕日韩欧美| 欧美精品第一页在线播放| 亚洲国产精品成人av| 成人免费观看网址| 岛国av一区二区在线在线观看| 色中色综合影院手机版在线观看| 色偷偷av亚洲男人的天堂| 久久精品视频网站| zzjj国产精品一区二区| 久久国产精品久久久| 午夜美女久久久久爽久久| 美女啪啪无遮挡免费久久网站| 久久精品视频导航| 97在线免费观看| 国产一区二区三区四区福利| 日韩成人中文字幕| 久久精品久久久久| 亚洲已满18点击进入在线看片| 亚洲a级在线播放观看| 久久亚洲精品中文字幕冲田杏梨| 国产精品香蕉在线观看| 欧美精品久久久久久久免费观看| 日韩一区视频在线| 成人av色在线观看| 欧美—级a级欧美特级ar全黄| 中文字幕久热精品在线视频| 国产精品一区二区电影| 亚洲精品xxxx| 国产91精品久久久久久| 亚洲夜晚福利在线观看| 欧美激情在线观看| 色爱av美腿丝袜综合粉嫩av| 日韩亚洲第一页| 欧美日韩激情小视频| 国产一区av在线| 欧美国产日本高清在线| 97成人精品视频在线观看| 色偷偷av一区二区三区| 欧美视频免费在线观看| 日韩精品视频在线观看网址| 亚洲人成在线观看网站高清| 亚洲欧美www| 欧美日韩国产在线播放| 国产欧美日韩免费| 亚洲偷欧美偷国内偷| 97精品一区二区视频在线观看| 亚洲第一天堂无码专区| 中文字幕少妇一区二区三区| 亚洲91精品在线观看| 中文字幕欧美在线| 久久久精品国产一区二区| 精品一区二区电影| 国产欧美日韩高清| 久久中文字幕在线| 黄色精品一区二区| 久久精品91久久香蕉加勒比| 精品国产乱码久久久久久虫虫漫画| 97精品伊人久久久大香线蕉| 伊人久久综合97精品| 亚洲电影中文字幕| 日韩三级影视基地| 久久国产精品久久久久| 日韩美女在线观看| 98视频在线噜噜噜国产| 奇米影视亚洲狠狠色| 国产精品成人免费视频| 亚洲国产精品久久久久久| 午夜精品免费视频| 亚洲无限乱码一二三四麻| 亚洲成人精品视频在线观看| 在线中文字幕日韩| 久久久久久久亚洲精品| 久久伊人91精品综合网站| 在线播放日韩av| 国产精品美女久久| 久久久久国产精品免费网站| 亚洲男人av在线| 成人福利免费观看| 国产精品美女在线| 中文国产成人精品| 国内精品久久久久久| 亚洲第一综合天堂另类专| 久久国产色av| 国产精品久久久久高潮| 亚洲精品久久久久久下一站| 91在线网站视频| 欧美一级电影免费在线观看| 中文字幕av一区| 少妇av一区二区三区| 亚洲的天堂在线中文字幕| 精品国产一区二区三区四区在线观看| 中文字幕在线看视频国产欧美| 日韩av在线直播| 亚洲国产中文字幕在线观看| 欧美性猛交xxxx黑人| 欧美日韩亚洲精品内裤| 亚洲第一视频在线观看| 欧美日韩福利在线观看| 久久欧美在线电影| 国产精品视频中文字幕91| 久久成人精品一区二区三区| 欧美黄色片免费观看| 国产精品影片在线观看| 国产亚洲xxx| 欧美精品一区在线播放| 亚洲高清免费观看高清完整版| 岛国视频午夜一区免费在线观看| 欧美精品18videos性欧美| 国产亚洲精品美女久久久| 92国产精品视频| 国产午夜精品全部视频在线播放| 日韩va亚洲va欧洲va国产| 国产日韩欧美91| 最近中文字幕mv在线一区二区三区四区| 亚洲欧美在线一区二区| 日韩av电影中文字幕| 欧美老女人性生活| 国产欧美日韩中文字幕在线| 亚洲日本欧美中文幕| 久久精品国产免费观看| 欧美日韩中国免费专区在线看| 精品亚洲aⅴ在线观看| 久久99青青精品免费观看| 国产精品视频一区二区三区四| 亚洲色图50p| 国产精品日韩欧美大师| 一区二区三区国产视频| 中文字幕精品www乱入免费视频| 中文字幕日韩专区| 日韩午夜在线视频| 成人精品在线视频| 国产91成人在在线播放| 久久亚洲精品成人| 日本精品视频在线播放| 日韩av在线看| 8050国产精品久久久久久| 欧美一级成年大片在线观看| 欧美激情一区二区三区成人| 亚洲人午夜精品| 欧美激情视频三区| 午夜精品久久久久久久男人的天堂| 久久久久久中文字幕|