Mixin是什麼概念?
在瀏覽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&
}
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&
// query from database
return new ArrayList&
}
}
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& 這裡使用組合模式,在 TaggableImpl 中實現打標籤的邏輯,然後讓 Post 類和 TaggableImpl 類都實現 Taggable 介面,Post 類中創建一個 TaggableImpl 實例並在實現 Taggable 時將相應方法調用委託過去。
return taggable.getTags(); // delegate
}
}
class TagMixin(object):
def add_tag(self, tag_id):
sql = ("insert into target_tagged"
" (target_id, target_kind, tag_id, creation_time) "
"values (?, ?, ?, CURRENT_TIMESTAMP)")
params = (self.ident, self.kind, tag_id)
storage.execute(sql, params)
storage.commit()
def get_tags(self):
sql = ("select tag_id, creation_time from target_tagged "
"where target_id = ? and target_kind = ?")
params = (self.ident, self.kind)
cursor = storage.execute(sql, params)
return cursor.fetchall()
class Post(Model, TagMixin):
kind = 1001
def __init__(self, ident, title):
self.ident = ident
self.title = title
def __repr__(self):
return "Post(%r, %r)" % (self.ident, self.title)
可以看出這裡多重繼承的用法是非常謹慎的:
- TagMixin 類是單一職責的
- TagMixin 類對宿主類(Post)一無所知,除了要求宿主類有 ident 和 kind 這兩個屬性(等價於 Java 中要求宿主類實現 Entity 介面)
- 宿主類的主體邏輯不會因為去掉 TagMixin 而受到影響,同時也不存在超類方法調用(super)以避免引入 MRO 查找順序問題
所以這樣比 Java 中的組合模式實現方式更加簡潔。同時因為使用得當,鑽石調用、MRO 查找順序等多重繼承的弊病也沒有被踩到。當然,這種 Duck Type 的設計也比顯式的介面約束對開發者有更高的要求,要求代碼中無 interface 而開發者腦海中有清晰的 interface。
Ruby 的 include 語法實現的 Mixin 也同理。趁著午休來答一個。
如樓上很多答主一樣,談到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 去了,不細說。要想了解具體的可以看一下《松本行弘的程序世界》這本書。Mixin 就是混入的意思。
和多重繼承類似(其實可以把 Mixin 看作多重繼承的一種在特定場景下的應用),但通常混入 Mixin 的類和 Mixin 類本身不是 is-a 的關係,混入 Mixin 類是為了添加某些(可選的)功能。自由地混入 Mixin 類就可以靈活地為被混入的類添加不同的功能。
傳統的「介面」概念中並不包含實現,而 Mixin 包含實現。實際上 Mixin 的作用和 Java 中的眾多以「able」結尾的介面很相似。不同的是 Mixin 提供了(默認)實現,而 Java 中實現了 -able 介面的類需要類自身來實現這些混入的功能(Serializable 介面是個例外)。這叫迷信方法,你想知道好處,打開py源碼,搜搜 mixin,試著不用迷信實現一個本來用了迷信的模塊,就能切身感受一下了。
在廖雪峰老師的網站上找到了這個解釋 多重繼承, 通俗易懂
mixin不是多繼承,mixin是duck type的糖,讓你可以不用去把一坨坨Interface繼承一遍然後才能彼此調用介面。
為了解決多重繼承的問題,Java引入了介面 (interface)技術,Lisp、Ruby引入了 Mix-in 技術。
以 Ruby 為例,Mix-in 有效地降低多重繼承複雜性(誰是你爹,哪個爹的優先順序高,你的把妹方法是繼承自哪個爹的等)。 Ruby中 Mix-in 的單位是 模塊 (module)。
Mix-in 技術按一下規則來限制多重繼承:- 繼承用但一繼承;
- 第二個及兩個以上的父類必須是 Mix-in 的抽象類。
- 不能單獨生成實例;
- 不能繼承普通類。
按照以上的原則,類在層次上具有單一繼承一樣的樹結構,同時又可以實現功能的共享(方法是:把共享的功能放在 Mix-in 類中,再把 Mix-in 類插入到樹結構里)。
Java 用 介面 解決規格繼承(類都有哪些方法)的問題,Mix-in 則解決了實現繼承(類中都用了什麼數據結構和什麼演算法)的問題。
逼逼了這麼多,對於 Mix-in 的理解是,Mix-in 只不過是實現多重繼承的一個技巧而已。就是編譯的時候把一段代碼複製到另一個地方的意思。
mix-in 是一個行為的集合,而這個行為可以被加到任意class里,然而在一些情況下,使用mix-in的類,需要滿足一些協議(contract)。
和介面的不同點,有兩個。
1. 如果有協議(contract)的話,協議是被聲明在mix-in的文件內的。也就是說,更容易復用。2. 介面,不包括如何實現。而mix-in是關於如何容易地重用實現的。「A mix-in is a collection of related behaviors that can be added to any class, although in some cases the class may have to fulfill a 「contract」 in order to use the mix-in」
「First, a mix-in is easier to reuse: the 「contract,」 if any, is specified in the mix-in』s documentation rather than being formally declared as a Java interface would be. Second, unlike a Java interface, which says nothing about how a class implements an interface, a mix-in is all about making it easy to reuse an implementation.」
Excerpt From: Fox, Armando. 「Engineering Long-Lasting Software: An Agile Approach Using SaaS and Cloud Computing, Beta Edition.」 iBooks.
說句不相關的,個人覺得,mix-in,相比面向對象來說,更像函數式,更關心能做什麼,而不是是什麼。Mixin是靜動態語言的組合模式的實現,只有方法,不實例化(所以Mixin class本身沒有成員函數)。其實是能實例化類的成員方法的delegate。由於動態語言天然多態的特性,實現比靜態語言簡單。《effective python》里item26專門介紹了Mixin。
個人感覺就是java介面
與plugin可被安裝到兼容的宿主程序的概念類似,mixin是一個class/object,它的屬性/方法可動態複製到其他適配的class/object。
參見 一般人不清楚的JavaScript概念 - mixin 和 MDN辭彙表 - Mixin
摘自effective java
Loosely speaking, a mixin is a type
that a class can implement in addition to its 「primary type」 to declare that it pro-
vides some optional behavior. For example, Comparable is a mixin interface that
allows a class to declare that its instances are ordered with respect to other mutually comparable objects
被約束的多重繼承。
Mixin是一種特殊的多重繼承,也就是多重繼承的子集。
使用Mixin的好處是,同時享有單一繼承的單純性和多重繼承的共有性。作為Mixin類,需要滿足以下條件:
- 不能單獨生成實例對象,屬於抽象類。
- 不能繼承Mixin以外的類。
因為有以上限制,Mixin類通常作為功能模塊使用,在需要該功能時「混入」,而且不會使類的關係變得複雜(比如,同名方法到底從哪個父類繼承)。
Java的介面,只提供了「規格」的多重繼承。Mixin類則同時提供了「規格」和「實現」的多重繼承,使用上相比介面會更加簡單。介面與mixin相同的地方是都可以多繼承,不同的地方在於 mixin 是帶實現的。簡單說就是:mixin 是帶實現的介面。
在編程中mixin是指為繼承它的class提供額外的功能, 但它自身卻不單獨使用的類. 在具有多繼承能力的編程語言中, mixin可以為類增加額外功能或方法. 在Django中, 使用mixin為class basic views提供更多的擴展。----從別的地方看到的
推薦閱讀:
※如何學習 Python,面對那麼多的標準庫,應該如何?
※Python 在改代碼時怎麼處理縮進問題?
※簡歷中如何證明自己的編程能力?
※怎樣才能寫出 Pythonic 的代碼?
TAG:Python |