HelloDart-MixIn,土話記錄多繼承機制

MixIn

MixIn是一種設計模式,旨在一個類中盡可能重用繼承多個超類的功能。
在絕大部分面向對象語言中,繼承與實現就是MixIn設計模式的實際應用。例如Java,一個子類可以單繼承一個超類,實現多個接口。到了JDK1.8之后,接口可以有默認方法和靜態方法,更優雅的實現MixIn設計模式。

Dart中的MixIn

在Dart官方文檔中,描述Dart是一門面向對象的語言,同時兼備融合了多種面向對象語言的優點。很自然的,MixIn模式也是Dart語言的特性之一。
在Dart中,類和接口是一類東西,但是implements關鍵字還是保留下來,也帶有實現的意思,implements的用法在文章下面再說。
Dart的多繼承,是由extends、with和implements來實現的。同時Dart中也保留了抽象類abstract的用法。
首先,抽象出一個實際的模型,用Dart去實現這個模型。

  1. 用意念創建一個交通工具抽象類,具有運輸方法
    //交通工具類,擁有運輸功能
    abstract class Transportation {
      //運輸功能
      void transport();
    }
    
  2. 默認創建3個子類去實現這個方法,分別是摩托車,自行車和小轎車,具體實現如下
    class Bicycle extends Transportation {
      @override
      void transport() {
        print(
            "Bicycle:\n動力組件: ${twoWheel()} > 安全指數: ${lowSafetyIndex()} > 動力來源:${bodyEnergy()}");
      }
    
      String lowSafetyIndex() => "low";
    
      String twoWheel() => "兩個輪子";
    
      String bodyEnergy() => "全靠腿登";
    }
    
    class Sedan extends Transportation {
      @override
      void transport() {
        print(
            "Sedan:\n動力組件: ${fourWheel()} > 安全指數: ${middleSafetyIndex()} > 動力來源:${gasolineEnergy()}");
      }
    
      String middleSafetyIndex() => "middle";
    
      String fourWheel() => "四個輪子";
    
      String gasolineEnergy() => "汽油";
    }
    
    class Motorcycle extends Transportation {
      @override
      void transport() {
        print(
            "Motorcycle:\n動力組件: ${twoWheel()} > 安全指數: ${lowSafetyIndex()} > 動力來源:${gasolineEnergy()}");
      }
    
      String lowSafetyIndex() => "low";
    
      String twoWheel() => "兩個個輪子";
    
      String gasolineEnergy() => "汽油";
    }
    
    
  3. 我們打印出來一下這3種交通工具的運輸方式方法
    void main() {
      Bicycle().transport();
      Sedan().transport();
      Motorcycle().transport();
    }
    
    打印如下
    Bicycle:
    動力組件: 兩個輪子 > 安全指數: low > 動力來源:全靠腿登
    Sedan:
    動力組件: 四個輪子 > 安全指數: middle > 動力來源:汽油
    Motorcycle:
    動力組件: 兩個輪子 > 安全指數: low > 動力來源:汽油
    
    上面的代碼中,Bicycle和Motorcycle的動力組件一樣是兩個輪子,安全指數都是low。
    而Sedan和Motorcycle的動力來源一樣是汽油,總結如下表
    相同類 相同的實現
    Bicycle&Motorcycle twoWheel()</br>lowSafetyIndex()
    Sedan&Motorcycle gasolineEnergy()
    我們可以發現這3個類,3個方法都是可復用的。在一些單繼承的語言里,只能利用多實現去復用這部分的代碼,
    而在Dart中,是可以多繼承的,不需要為了實現而多寫一些冗余的代碼。

extends 與 with

同絕大部分的面向對象語言,在Dart中繼承一個超類使用extends關鍵字。
在上面的交通工具例子中,根據動力組件,安全系數和動力來源可以封裝出來以下六個類

//雙輪交通工具
class TwoWheelTransportation {
  String powerUnit() => "兩個輪子";
}

//汽油能源交通工具
class GasolineEnergyTransportation {
  String energy() => "汽油";
}

//四輪交通工具,一般來說安全性能為中
class FourWheelTransportation {
  String powerUnit() => "四個輪子";

}

//安全指數中等的交通工具兒
class MiddleSafetyIndex{
  String safetyIndex() => "middle";
}

//安全指數低的交通工具兒
class LowSafetyIndex{
  String safetyIndex() => "low";
}

//人力發動機
class BodyEnergyTransportation {
  String energy() => "全靠腿登";
}

然后將Bicycle、Motorcycle、Sedan類的代碼改成一下模樣

class Bicycle extends Transportation
    with TwoWheelTransportation, BodyEnergyTransportation, LowSafetyIndex {
  @override
  void transport() {
    print(
        "Bicycle:\n動力組件: ${powerUnit()} > 安全指數: ${safetyIndex()} > 動力來源:${energy()}");
  }
}

class Motorcycle extends Transportation
    with TwoWheelTransportation, GasolineEnergyTransportation, LowSafetyIndex {
  @override
  void transport() {
    print(
        "Motorcycle:\n動力組件: ${powerUnit()} > 安全指數: ${safetyIndex()} > 動力來源:${energy()}");
  }
}

class Sedan extends Transportation
    with
        GasolineEnergyTransportation,
        FourWheelTransportation,
        MiddleSafetyIndex {
  @override
  void transport() {
    print(
        "Sedan:\n動力組件: ${powerUnit()} > 安全指數: ${safetyIndex()} > 動力來源:${energy()}");
  }
}

打印結果也是相同

Bicycle:
動力組件: 兩個輪子 > 安全指數: low > 動力來源:全靠腿登
Sedan:
動力組件: 四個輪子 > 安全指數: middle > 動力來源:汽油
Motorcycle:
動力組件: 兩個輪子 > 安全指數: low > 動力來源:汽油

以上的寫法,省略了相同的safetyIndex方法、powerUnit方法和energy方法的重復代碼,便于維護。
再舉個栗子,例如有個大興趣發明家發明了一輛四輪的,動力全靠腿蹬的、低安全性的木質汽車。
在抽象這個WoodenSedan的時候,外觀最接近的應該是Sedan類,先讓WoodenSedan繼承Sedan類,打印結果如下

class WoodenSedan extends Sedan {
  @override
  void transport() {
    print(
        "WoodenSedan:\n動力組件: ${powerUnit()} > 安全指數: ${safetyIndex()} > 動力來源:${energy()}");
  }
}
WoodenSedan:
動力組件: 四個輪子 > 安全指數: middle > 動力來源:汽油

這個時候,如果是在Java中就需要以多實現的方式去重寫safetyIndexenergy方法了。而Dart中只需要使用with關鍵字在extends后面添加超類,就可以繼承safetyIndexenergy方法了

class WoodenSedan extends Sedan with LowSafetyIndex,BodyEnergyTransportation{
  @override
  void transport() {
    print(
        "WoodenSedan:\n動力組件: ${powerUnit()} > 安全指數: ${safetyIndex()} > 動力來源:${energy()}");
  }
}
WoodenSedan:
動力組件: 四個輪子 > 安全指數: low > 動力來源:全靠腿登

但是,當一個子類所繼承的不同超類擁有相同簽名的方法的時候,子類實際使用的方法跟超類的位置有關系。

with的順序問題與implements

  • with的順序問題
    雖說with的寫法很方便,但是還是有一些問題需要注意的。如果2個或多個超類擁有相同簽名的A方法,那么子類會以繼承的最后一個超類中的A方法為準。當然這是子類沒有重寫A方法的前提下,如果子類自己重寫了A方法則以本身的A方法為準。
    可參照上述例子的打印結果
    class WoodenSedan extends Sedan with LowSafetyIndex,GasolineEnergyTransportation,BodyEnergyTransportation{
      @override
      void transport() {
        print(
            "WoodenSedan:\n動力組件: ${powerUnit()} > 安全指數: ${safetyIndex()} > 動力來源:${energy()}");
      }
    }
    WoodenSedan:
    動力組件: 四個輪子 > 安全指數: low > 動力來源:全靠腿登
    
    WoodenSedan類繼承了汽油動力類和體力動力類,但是由于BodyEnergyTransportation位置比GasolineEnergyTransportation靠右,所以實際其作用的還是BodyEnergyTransportation的方法。
  • implements:不同于with的直接繼承。implements要求子類必須擁有所繼承超類中的所有方法,我們可以將直接認為是Java中的implements關鍵字。
    如果將上述WoodenSedan繼承多個類的關鍵字改為implements打印結果如下
    class WoodenSedan extends Sedan implements LowSafetyIndex,GasolineEnergyTransportation,BodyEnergyTransportation{
      @override
      void transport() {
        print(
            "WoodenSedan:\n動力組件: ${powerUnit()} > 安全指數: ${safetyIndex()} > 動力來源:${energy()}");
      }
    }
    WoodenSedan:
    動力組件: 四個輪子 > 安全指數: middle > 動力來源:汽油
    
    動力來源超類和安全屬性超類的方法都不起作用了,取而代之的是Sedan超類中的方法被打印。如果WoodenSedan不繼承Sedan超類,則會編譯報錯

總結

implements多用于強調需要重寫的場合,而extendswith則多是為了直接繼承。在編碼過程中,區分好他們implementswith的關系也能使得代碼更加易于閱讀與維護

感謝一下文章作者的無私分享,排名不分前后
參考文章
參考文章
參考文章

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,797評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,179評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,628評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,642評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,444評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,948評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,040評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,185評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,717評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,602評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,794評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,316評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,045評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,418評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,671評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,414評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,750評論 2 370