1. 程式人生 > 程式設計 >javascript設計模式之鴨子型別和多型

javascript設計模式之鴨子型別和多型

目錄
  • 1.鴨子型別
  • 2.多型
    • 2.1 多型
    • 2.2 多型
  • 總結

    本文參考曾探編寫的設計模式與開發實踐

    設計模式的實現都遵循一條原則,即“找出程式中變化的地方,並將變化封裝起來”。一個程式的設計總是可以分為可變的部分和不變的部分。當我們找出可變的部分,並且把這些部分封裝起來,那麼剩下的就是不變和穩定的部分。這些不變和穩定的部分是非常容易複用的。這也是設計模式為什麼描寫的是可複用面向物件軟體基礎的原因。

    1.鴨子型別

    鴨子型別的通俗說法是:“如果它走起路來像鴨子,叫起來也是鴨子,那麼它就是鴨子。”

    鴨子型別專業解釋:例如,在不使用鴨子型別的語言中,我們可以編寫一個函式,它接受一個型別為"鴨子"的物件,並呼叫它的"走"和"叫"方法,但它只能接受鴨子型別的物件,否則報錯。在使用鴨子型別的語言中,這樣的一個函式可以接受一個任意型別的物件,並呼叫它的"走"和"叫"方法。如果這些需要被呼叫的方法不存在,那麼將引發一個執行時錯誤。任何擁有這樣的正確的"走"和"叫"方法的物件都可被函式呼叫這種行為就是符合鴨子型別。

    所以如果弱型別語言(js)函式需要接收引數,為保證健壯性,則應先判斷引數型別,並判斷引數是否包含需要訪問的方法、屬性。只有當這些條件滿足時,程式才真正處理呼叫引數的方法、參http://www.cppcns.com

        var duck = {
                sing: function() {
                    console.log('嘎嘎嘎');
                }
            }
            var chicken = {
                sing: function() {
                    console.log('嘎嘎嘎');
                }
            }
            var choir = [] //合唱團
            function joinChoir(duck) {
                if (duck && typeof duck.sing === 'function') {
                    choir.push(duck)
                    console.log('合唱隊添加了一個成員');
                }
            }
            joinChoir(duck)
            joinChoir(chicken)
                // 大合唱
            for (let i = 0; i < choir.length; i++) {
                choir[i].sing()
            }
    

    2.多型

    2.1 java多型

    對面向物件來說,多型分為編譯時多型和執行時多型。其中編譯時多型是靜態的,主要是指方法的過載,它是根據引數列表的不同來區分不同的方法。通過編譯之後會變成兩個不同的方法,在執行時談不上多型。而執行時多型是動態的,它是通過動態繫結來實現的,也就是大家通常所說的多型性。

    在java裡,多型是同一個行為具有不同表現形式或形態的能力,即http://www.cppcns.com物件多種表現形式的體現,就是指程式中定義的引用變數所指向的具體型別和通過該引用變數發出的方法呼叫在時並不確定,而是在程式執行期間才確定,即一個引用變數倒底會指向哪個類的例項物件,該引用變數發出的方法呼叫到底是哪個類中實現的方法,必須在由程式執行期間才能決定。在簡單來說,編譯時物件是父類型別,到真正執行時,物件才可以知www.cppcns.com

    道具體是哪個子類型別,才知道呼叫哪個子類中實現的方法

    Java 實現多型有 3 個必要條件:繼承、重寫和向上轉型。只有滿足這 3 個條件,開發人員才能夠在同一個繼承結構中使用統一的邏輯實現程式碼處理不同的物件,從而執行不同的行為。

    • 繼承:在多型中必須存在有繼承關係的子類和父類。
    • 重寫:子類對父類中某些方法進行重新定義,在呼叫這些方法時就會呼叫子類的方法。
    • 向上轉型:在多型中需要將子類的引用賦給父類物件,只有這樣該引用才既能可以呼叫父類的方法,又能呼叫子類的方法。

    由此可以得出使用多型的好處,我們可以很好的完成程式碼的解耦和工作,加強程式碼的可擴充套件性,是程式碼更加靈活,在不改變原有介面方法的情況下簡化流程等,總結一下就是:

    • 減耦合
    • 增強可以替換性
    • 可擴充套件性
    • 靈活性等…

    舉個生活例子,如下圖所示:使用手機掃描二維碼支付時,二維碼並不知道客戶是通過何種方式進行支付,只有通過二維碼後才能判斷是走哪種支付方式執行對應流程。

    在這裡插入圖片描述

    舉個程式碼例子,建立 Figure 類,在該類中首先定義儲存二維物件的尺寸,然後定義有兩個引數的構造方法,最後新增 area() 方法,該方法計算物件的面積。程式碼如下:

    public class Figure {
        double dim1;
        double dim2;
        Figure(double d1,double d2) {
            // 有JbdCtZ參的構造方法
            this.dim1 = d1;
            this.dim2 = d2;
        }
        double area() {
            // 用於計算物件的面積
            System.out.println("父類中計算物件面積的方法,沒有實際意義,需要在子類中重寫。");
            return 0;
        }
    }
    

    建立繼承自 Figure 類的 Rectangle 子類,該類呼叫父類的構造方法,並且重寫父類中的 area() 方法。程式碼如下:

    public class Figure {
        double dim1;
        double dim2;
        Figure(double d1,double d2) {
            // 有參的構造方法
            this.dim1 = d1;
            this.dim2 = d2;
        }
        double area() {
            // 用於計算物件的面積
            System.out.println("父類中計算物件面積的方法,沒有實際意義,需要在子類中重寫。");
            return 0;
        }
    }
    

    建立繼承自 Figure 類的 Triangle 子類,該類與 Rectangle 相似。程式碼如下:

    public class Rectangle extends Figure {
        Rectangle(double d1,double d2) {
            super(d1,d2);
        }
        double area() {
            System.out.println("長方形的面積:");
            return super.dim1 * super.dim2;
        }
    }
    

    建立 Test 測試類,在該類的 main() 方法中首先宣告 Figure 類的變數 figure,然後分別為 figure 變數指定不同的物件,並呼叫這些物件的 area() 方法。程式碼如下:

    public class Rectangle extends Figure {
        Rectangle(double d1,d2);
        }
        double area() {
            System.out.println("長方形的面積:");
            return super.dim1 * super.dim2;
        }
    }
    

    從上述程式碼可以發現,無論 figure 變數的物件是 Rectangle 還是 Triangle,它們都是 Figure 類的子類,因此可以向上轉型為該類,從而實現多型。

    2.2 js多型

    多型的實際含義:是同一操作作用於不同的物件上面,可以產生不同的解釋和不同的執行結果。換句話說,給不同的物件傳送同一個訊息的時候,這些物件會根據這個訊息www.cppcns.com分別給出不同的反饋。

    來舉例說明一下多型的實際含義:

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

          var Duck = function() {
            }
            Duck.prototype.sing = function() {
                console.log('嘎嘎嘎');
            }
            var Chicken = function() {
            }
            Chicken.prototype.sing = function() {
                console.log('嘎嘎嘎');
            }
            function sing(animal) {
                if (animal && (typeof animal.sing === 'function')) {
                    animal.sing()
                }
            }
            sing(new Duck())
            sing(new Chicken())
    

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

    多型的實現:多型的思想實際上是把“做什麼”和“誰去做”分離開來,要實現這一點,歸根結底先要消除型別之間的耦合關係。如果型別之間的耦合關係沒有被消除,那麼我們在makeSound 方法中指定了發出叫聲的物件是某個型別,它就不可能再被替換為另外一個型別。在 Java 中,可以通過向上轉型來實現多型。而 JavaScript 的變數型別在執行期是可變的。一個JavaScript 物件,既可以表示 Duck 型別的物件,又可以表示 Chicken 型別的物件,這意味著 JavaScript 物件的多型性是與生俱來的。這種與生俱來的多型性並不難解釋。JavaScript 作為一門動態型別語言,它在編譯時沒有型別檢查的過程,既沒有檢查建立的物件型別,又沒有檢查傳遞的引數型別。

    多型的最根本好處:你不必再向物件詢問“你是什麼型別”而後根據得到的答案呼叫物件的某個行為——你只管呼叫該行為就是了,其他的一切多型機制都會為你安排妥當。換句話說,多型最根本的作用就是通過把過程化的條件分支語句轉化為物件的多型性,從而消除這些條件分支語句

    多型的最根本好處,可以用下面這個例子很好地詮釋:在電影的拍攝現場,當導演喊出“action”時,主角開始背臺詞,照明師負責打燈光,後面的群眾演員假裝中槍倒地,道具師往鏡頭裡撒上雪花。在得到同一個訊息時,每個物件都知道自己應該做什麼。如果不利用物件的多型性,而是用面向過程的方式來編寫這一段程式碼,那麼相當於在電影開始拍攝之後,導演每次都要走到每個人的面前,確認它們的職業分工(型別),然後告訴他們要做什麼。如果對映到程式中,那麼程式中將充斥著條件分支語句。利用物件的多型性,導演在釋出訊息時,就不必考慮各個物件接到訊息後應該做什麼。物件應該做什麼並不是臨時決定的,而是已經事先約定和排練完畢的。每個物件應該做什麼,已經成為了該物件的一個方法,被安裝在物件的內部,每個物件負責它們自己的行為。所以這些物件可以根據同一個訊息,有條不紊地分別進行各自的工作。

    將行為分佈在各個物件中,並讓這些物件各自負責自己的行為,這正是面向物件設計的優點。

    總結

    本篇文章就到這裡了,希望能夠給你帶來幫助,也希望您能夠多多關注我們的更多內容!