理解Dart的Mixin繼承機制

Dart語言集合了現代編程語言的衆多優勢,Mixin繼承機制也是其一。但針對Java程序員來講,可能不是一會兒能理解的,好比我第一次看到的時候,也迷迷糊糊了半天——這是啥玩意???java

要說Mixin,可能寫成MixIn會更好理解,翻譯回來就是混入,固然你執意說這是一種「迷信」繼承機制,那也沒轍。git

下面將從一個實際情景入手,對比Java和Dart的實現,以便更好理解Dart的mixin。程序員

場景

咱們先來描繪這麼一個職業關係圖:
github

職業關係圖編程

從圖中能夠梳理出如下關係:編程語言

  • 工程師類目,有軟件工程師和建築工程師師等,他們共同點都是工程師
  • 教師類目有美術教師,IT教師等,他們共同點都是教師

而以上的工程師、教師都是社會的工做者。ide

那麼接下來咱們分別使用Java和Dart來實現這個關係類。ui

注意:接下來的這篇文章中爲了方便表達意義,將全部類寫在一個文件裏面,請暫時忽略代碼不規範細節。而且Dart在本文不會使用語法糖寫法。spa

Java版本實現

從層級出發,能夠寫出Java版本實現以下:翻譯

//工做者
abstract class Worker {
    public abstract void doWork();//工做者須要工做
}

//工程師
class Engineer extends Worker {
    @Override
    public void doWork() {
        System.out.println("工程師在工做");
    }
}

//教師
class Teacher extends Worker {
    @Override
    public void doWork() {
        System.out.println("教師在教學");
    }
}

//軟件工程師
class SoftwareEngineer extends Engineer {

}

//建築工程師
class BuildingEngineer extends Engineer {

}

//美術教師
class ArtTeacher extends Teacher {

}

//IT教師
class ITTeacher extends Teacher {

}


Dart版本實現

//工做者
abstract class Worker {
  void doWork();//工做者須要工做
}

//工程師
class Engineer extends Worker {
  void doWork() {
    print('工程師在工做');
  }
}

//教師
class Teacher extends Worker {
  void doWork() {
    print('教師在教學');
  }
}

//軟件工程師
class SoftwareEngineer extends Engineer {

}

//建築工程師
class BuildingEngineer extends Engineer {

}

//美術教師
class ArtTeacher extends Teacher {

}

//IT教師
class ITTeacher extends Teacher {

}

從上面實現能夠看出,兩個實現並沒什麼卵區別好咩。。。。。。

嗯,目前來講確實是這樣的,由於Dart也是單繼承。

由於上面的場景是在是too young too simple了,下面開始擴展一些場景。

場景擴展

仍是剛剛那張關係圖,咱們開始思考這些職業他們都具備什麼能力。

因而我給這些職業虛擬瞭如下能力:

職業 能力
軟件工程師 軟件設計、修電腦
建築工程師 手繪
美術教師 手繪、書法
IT教師 修電腦

他們的關係圖以下:

經過圖形或表格能夠看出,軟件工程師和IT教師都具有修電腦的能力,建築工程師和美術教師都具有手繪的能力,可是這些能力都是他們特有的,不是工程師或者教師具有的能力,因此不能在他們的父類中實現。

那麼這個時候咱們就考慮到一個東西——接口。

以軟件工程師和IT教師爲例:

他們都具有修電腦的能力:

interface CanFixComputer {
    void fixComputer();
}

interface CanDesignSoftware {
    void designSoftware();
}

//軟件工程師
class SoftwareEngineer extends Engineer implements CanFixComputer, CanDesignSoftware {

    @Override
    public void fixComputer() {
        System.out.println("修電腦");
    }

    @Override
    public void designSoftware() {
        System.out.println("設計軟件");
    }
}

//IT教師
class ITTeacher extends Teacher implements CanFixComputer {

    @Override
    public void fixComputer() {
        System.out.println("修電腦");
    }
}

  至關標準的實現了

  咱們知道Dart是沒有interface這種東西的,但並不覺得着這門語言沒有接口,事實上,Dart任何一個類都是接口,你能夠實現任何一個類,只須要重寫那個類裏面的全部具體方法。

    咱們只須要將上面的interface 修改爲 abstract class,就是dart中的實現了。

    可是咱們發現,fixComputer這個接口被繼承了兩次,而且兩次的實現都是同樣的,這裏就出現了代碼重複和冗餘的問題。怎麼辦呢?在java中咱們有接口的default實現來解決這個問題(這是一個java8出現的不得已的方案。)

    這個時候mixin的做用就出現了

abstract class CanFixComputer {
  void fixComputer() {
    print('修電腦');
  }
}

abstract class CanDesignSoftware {
  void designSoftware() {
    print('設計軟件');
  }
}

//軟件工程師
class SoftwareEngineer extends Engineer
    with CanFixComputer, CanDesignSoftware {

}

//IT教師
class ITTeacher extends Teacher with CanFixComputer {

}

main() {
  ITTeacher itTeacher = new ITTeacher();
  itTeacher.doWork();
  itTeacher.fixComputer();
  SoftwareEngineer softwareEngineer = new SoftwareEngineer();
  softwareEngineer.doWork();
  softwareEngineer.fixComputer();
  softwareEngineer.designSoftware();
}

能夠看到,這裏再也不用implements,更不是extends,而是with

      並且,每一個具備某項特性的類再也不須要具體去實現一樣的功能,接口是無法實現功能的,而經過繼承的方式雖然能實現功能,但已經有父類,同時不是一個父類,又不能多繼承,因此這個時候,Dart的Mixin機制就比Java的接口會高效,開發上層的只須要關心當前須要什麼特性,而開發功能模塊的關心具體要實現什麼功能。

順序的理解

既然是with,那應該也會有順序的區別,
思考一個問題:若是同時with兩個類,但兩個類中有一樣的一個方法的不一樣實現,那麼這個時候應該使用的是哪個類的方法?

下面以一個簡單的Demo來講明這個問題:

class First {
  void doPrint() {
    print('First');
  }
}

class Second {
  void doPrint() {
    print('Second');
  }
}

class Father {
  void doPrint() {
    print('Father');
  }
}

class Son1 extends Father with First,Second {
  void doPrint() {
    print('Son1');
  }
}

class Son2 extends Father with First implements Second {
  void doPrint() {
    print('Son2');
  }
}

main() {
  Son1 son1 = new Son1();
  son1.doPrint();
  Son2 son2 = new Son2();
  son2.doPrint();
}

那麼這個程序運行後,將會在控制檯輸出以下:

1
2
Son1
Son2

能夠看到,不管是extends、implements仍是mixin,優先級最高的是在具體類中的方法。

咱們稍微改一下上面的例子:

class First {
  void doPrint() {
    print('First');
  }
}

class Second {
  void doPrint() {
    print('Second');
  }
}

class Father {
  void doPrint() {
    print('Father');
  }
}

class Son1 extends Father with First,Second {

}

class Son2 extends Father with First implements Second {

}

main() {
  Son1 son1 = new Son1();
  son1.doPrint();
  Son2 son2 = new Son2();
  son2.doPrint();
}

這個時候控制檯輸出以下:

1
2
Second
First

能夠看到,其實在Son2中implements只是說要實現他的doPrint()方法,這個時候其實具體實現是First中Mixin了具體實現。
而Mixin的具體順序也是能夠從代碼倒過來看的,最後mixin的優先級是最高的。

PS: dart中有個關鍵字 mixin 能夠用來替換上面的 class,這個類表示專門用來作mixin的。

另外在mixin類中咱們還以使用這樣使用

mixin MusicalPerformer on Musician {
  // ···
}

這個表示只有類Musician可以使用這個mixin類。

相關文章
相關標籤/搜索