在瀏覽tornado的代碼時,auth中的類都以Mixin命名,這個詞好奇怪啊,查了一下資料,有人解釋Mixin為mix in,混入的意思,類似於多重繼承。auth模組實現OpenID和OAuth,為什麼要用Mixin方式?Mixin的應用情境?與“介面”概念有什麼區別?
回複內容:
Mixin 實質上是利用語言特性(比如 Ruby 的 include 文法、Python 的多重繼承)來更簡潔地實現組合模式
。
以如下 Java 偽碼為例,實現一個可複用的“打標籤”組件(Taggable),並且應用到文章(Post)模型上:
import java.util.List;import java.util.ArrayList;interface Entity { public int getId(); public int getKind();}interface Taggable { public void addTag(int tagId); public List<Integer> getTags();}class TaggableImpl implements Taggable { private Entity target; public TaggableImpl(Entity target) { this.target = target; } public void addTag(int tagId) { int id = target.getId(); int kind = target.getKind(); System.out.println("insert into ... values " + id + ", " + kind + ", " + tagId + ")"); } public ArrayList<Integer> getTags() { // query from database return new ArrayList<Integer>(); }}class Post implements Entity, Taggable { public final static int KIND = 1001; private Taggable taggable; private int id; private String title; public Post(int id, String title) { this.id = id; this.title = title; this.taggable = new TaggableImpl(this); } public int getId() { return id; } public int getKind() { return KIND; } public void addTag(int tagId) { taggable.addTag(tagId); // delegate } public ArrayList<Integer> getTags() { return taggable.getTags(); // delegate }}
Mixin 就是混入的意思。
和多重繼承類似(其實可以把 Mixin 看作多重繼承的一種在特定情境下的應用),但通常混入 Mixin 的類和 Mixin 類本身不是
is-a 的關係,混入 Mixin 類是為了添加某些(可選的)功能。自由地混入 Mixin 類就可以靈活地為被混入的類添加不同的功能。
傳統的「介面」概念中並不包含實現,而 Mixin 包含實現。實際上 Mixin 的作用和 Java 中的眾多以「
able」結尾的介面很相似。不同的是 Mixin 提供了(預設)實現,而 Java 中實現了 -able 介面的類需要類自身來實現這些混入的功能(Serializable 介面是個例外)。趁著午休來答一個。
如樓上很多答主一樣,談到Mixin就不得不談到多重繼承,因為Mixin的出現就是為瞭解決多重繼承的問題,那麼多重繼承有什麼問題呢?
在《松本行弘的程式世界》一書中,作者列舉了以下三點:
- 結構複雜化:如果是單一繼承,一個類的父類是什麼,父類的父類是什麼,都很明確,因為只有單一的繼承關係,然而如果是多重繼承的話,一個類有多個父類,這些父類又有自己的父類,那麼類之間的關係就很複雜了。
- 優先順序模糊:假如我有A,C類同時繼承了基類,B類繼承了A類,然後D類又同時繼承了B和C類,所以D類繼承父類的方法的順序應該是D、B、A、C還是D、B、C、A,或者是其他的順序,很不明確。
- 功能衝突:因為多重繼承有多個父類,所以當不同的父類中有相同的方法是就會產生衝突。如果B類和C類同時又有相同的方法時,D繼承的是哪個方法就不明確了,因為存在兩種可能性。
當然你可以說有些語言解決了這個問題,但是並不是所有語言都想要去糾結這個問題。
所以為能夠利用多繼承的優點又解決多繼承的問題,提出了規格繼承和實現繼承這兩樣東西。
簡單來講,規格繼承指的是一堆方法名的集合,而實現繼承除了方法名還允許有方法的實現。
Java 選擇了規格繼承,在 Java 中叫 interface(不過Java8中已經有預設方法了),而 Ruby 選擇了實現繼承,也可以叫Mixin,在 Ruby 中叫 module。
從某種程度上來說,繼承強調 I am,Mixin 強調 I can。當你 implement 了這個介面或者 include 這個 module 的時候,然後就你行你上。
所以這又可以扯到 duck typing 去了,不細說。要想瞭解具體的可以看一下《松本行弘的程式世界》這本書。這叫迷信方法,你想知道好處,開啟py源碼,搜搜 mixin,試著不用迷信實現一個本來用了迷信的模組,就能切身感受一下了。
為瞭解決多重繼承的問題,Java引入了介面 (interface)技術,Lisp、Ruby引入了 Mix-in 技術。
以 Ruby 為例,Mix-in 有效地降低多重繼承複雜性(誰是你爹,哪個爹的優先順序高,你的把妹
方法是繼承自哪個爹的等)。 Ruby中 Mix-in 的單位是
模組 (module)。
Mix-in 技術按一下規則來限制多重繼承:
- 繼承用但一繼承;
- 第二個及兩個以上的父類必須是 Mix-in 的抽象類別。
Mix-in 類是具有以下特徵的抽象類別:
- 不能單獨產生執行個體;
- 不能繼承普通類。
按照以上的原則,類在層次上具有單一繼承一樣的樹結構,同時又可以實現功能的共用(方法是:把共用的功能放在 Mix-in 類中,再把 Mix-in 類插入到樹結構裡)。
Java 用 介面 解決
規格繼承(類都有哪些方法)的問題,Mix-in 則解決了
實現繼承(類中都用了什麼資料結構和什麼演算法)的問題。
逼逼了這麼多,對於 Mix-in 的理解是,
Mix-in 只不過是實現多重繼承的一個技巧而已。被約束的多重繼承。mixin不是多繼承,mixin是duck type的糖,讓你可以不用去把一坨坨Interface繼承一遍然後才能彼此調用介面。mixin 並沒有特別權威的標準,非要讓我下個定論的話:mixin 其實就是在語言不提供標準多重繼承的情況下,變相實現多重繼承的一個文法糖。 不同版本的 mixin 實現不太一樣,但出發點都是在允許繼承介面和繼承實現的基礎上,簡化繼承關係,避免多重繼承的坑。
我認為 C# 的擴充方法其實就是一種 mixin,但又不是通過代碼拷貝等動態特性實現的,而是在編譯器層面很嚴格的幫你進行了封裝。對其它靜態語言來說,如果沒有多重繼承,要自己實現 mixin 是很困難的。動態語言有些就支援 mixin,不支援的話自己造一個也很容易。
我最近也利用 javascript 實現了一個版本的 mixin 機制,為了支援可視化編輯,添加了類似組件系統的能力,將整合進 0.5 的 Fireball-x,到時會進一步更新答案。
手機碼字求贊,歡迎更多討論。就是編譯的時候把一段代碼複製到另一個地方的意思。Mixin是一種特殊的多重繼承,也就是多重繼承的子集。
使用Mixin的好處是,同時享有單一繼承的單純性和多重繼承的共有性。
作為Mixin類,需要滿足以下條件:
- 不能單獨產生執行個體對象,屬於抽象類別。
- 不能繼承Mixin以外的類。
因為有以上限制,Mixin類通常作為功能模組使用,在需要該功能時“混入”,而且不會使類的關係變得複雜(比如,同名方法到底從哪個父類繼承)。
Java的介面,只提供了“規格”的多重繼承。Mixin類則同時提供了“規格”和“實現”的多重繼承,使用上相比介面會更加簡單。