什么是Mixin模式:帶實現的協議


  Mixin(織入)模式並不是GOF的《設計模式》歸納中的一種,但是在各種語言以及框架都會發現該模式(或者思想)的一些應用。簡單來說,Mixin是帶有全部實現或者部分實現的接口,其主要作用是更好的代碼復用。本文將介紹Mixin的應用場景,以及關於繼承、組合、多繼承、接口的一些思考。
 

相關概念:

  前面提到,Mixin是有部分或者全部實現的接口,其主要作用是代碼復用,需要理解這個簡單的描述,需要先理清一些概念。

繼承與組合:

  繼承是面向對象的三大特征(封裝、繼承、多態),如果類A繼承自類B,那么我們稱A為子類(派生類),稱B為父類(基類)。什么時候類A才能繼承類B呢,可以說A是B的一種特殊化,英語來說就是 A is a B,或者A is a kind of B。比如狗(dog)和動物(Animal)這兩個抽象,dog is animal,這個是成立的,所以dog可以繼承自animal。
  而組合代表的是其中一個類的對象是另一個類的對象的組成組合,英語來說,“ has a”,比如人(People)這個類有一個屬性addr是一個地址類(Address)的實例,就是說每個人都有一個地址。
  不管是繼承還是組合,都起到了代碼復用的作用,但又各有優缺點。上面繼承和組合的例子都很明顯,但有些情況就不那么容易區分兩類事物是繼承還是組合的關系了,或者說,兩個類之間既可以用繼承,又可以用組合,比如設計模式中的adapter模式,既可以類適配,又可以組合適配(對象適配)。
 

多繼承與接口:

  在使用編程語言抽象事物之間的繼承關系的時候,需要考慮對多繼承的實現。所謂多繼承就是說一個類有多個基類,舉個簡單的例子,dog是animal,同時dog又是runnable(可以跑動的對象)。多繼承對於人類的思維來說是比較正常直觀的,但是對於計算機編程語言,都會遇到一個繞不過去的問題,那就是菱形繼承(diamond problem),下面這個圖形象展示了什么時菱形繼承:
  
  從上圖可以看到,類B、類C都繼承了類A,類D同時繼承了類B和類C,在繼承關系上就形成了 “菱形”--類D有兩條路徑到達類A。菱形問題帶來什么問題呢,如果類A定義了某個方法foo,而且B和C都沒有重寫該方法,那么B和C都會有某種機制找到foo,那么對於D的實例在調用foo方法的時候,是調用到B中指向的foo方法還是C中指向的foo方法呢?
  菱形繼承的問題會影響到語言的設計,一些編程語言支持多繼承,如C++、python等等;另外一些則不支持多繼承,如Java,ruby等。對於支持多繼承的語言,為了解決菱形繼承的問題,一般都會使用特定的方法,比如C++中的虛繼承,python中的新式類的MRO。而對於不支持多繼承的語言,一般使用某種接口(或者約定、協議)來實現多繼承的功能,如Java中的interface,ruby中的include module。
 

duck typing:

  一個事物是不是鴨子(duck),如果它走起來像一只鴨子、叫起來也像一只鴨子,即從表現來看像一只鴨子,那么我們就認為它是一只鴨子。把這種思想應用到編程中,就是duck typing,簡而言之,一個約定要求必須實現某些功能,而某個類實現了這個功能,就可以把這個類當做約定的具體實現來使用。duck typing的思想在編程語言中的使用非常廣泛,如Java中的Interface,Python中的各種protocol,ruby中的include module。
  上面提到協議(泛指接口、預定、duck typing,下同)可以用來實現多繼承的功能,在以下情況特別合適:“基類”代表的其實是一種能力或者承諾。比如前面dog同時繼承animal和runnale,但跑(run)就是一種能力的體現,這個時候就可以把runable作為一個協議,dog是可以跑的,所以應該實現run方法,也就遵循了這個協議,那么dog就是一個runable了。從例子可以看出, 如果一個所謂的“基類”事實上代表了某種能力(it can,xxxable)的時候,使用協議比多繼承是更佳合理的選擇。
 

Mixin:

  在前面說清楚了各個概念之后,我們來看看Mixin到表代表了什么,不過再次回顧上面一段提到的java interface和python protocol,這二者本身是沒有任何實現的,都是需要使用者來實現相應的方法。Mixin本身也是一種能力的承諾,但 Mixin不同的不同之處在於Mixin是有部分或者全部實現的,在Mixin中的實現有利於代碼復用。如果是部分實現,那么就是在Mixin中實現整個流程,而實現Mixin約定的類提供關鍵的、該類特有的方法,這有點類似模板模式,也是依賴倒置原則的體現。
  不同的語言或者框架中,對Mixin模式有不同的實現形式,python中,除了protocol,也可以用多繼承的形式來實現Mixin,為了區分普通的多繼承,Mixin類的類名一般都會帶上后綴:“Mixin”,比如python lib里面的兩個Mixin類:UserDict.DictMixin和SocketServer.ForkingMixIn。DictMixin類包括部分實現,使用者只要實現幾個核心的函數接口就行了。
  Mixin defining all dictionary methods for classes that already have a minimum dictionary interface including getitem, setitem, delitem,and keys. 
  而python中的SocketServer.ForkingMixIn有全部的實現,所以使用者無需特殊處理,就擁有了fork帶來的好處,例如
  
1 class ForkingUDPServer(ForkingMixIn, UDPServer): pass
2 class ForkingTCPServer(ForkingMixIn, TCPServer): pass
  在python的一些框架中,也有Mixin的身影,如tornado。
  在ruby中,並不直接使用Mixin這個單詞,而是使用在類的聲明中include 一個module的辦法,如下面的代碼(來自wiki):
 1 class Student
 2   include Comparable # The class Student inherits Comparable module using include keyword
 3   attr_accessor :name, :score
 4 
 5   def initialize(name, score)
 6     @name = name
 7     @score = score
 8   end
 9 
10   # Including the Comparison module, requires the implementing class to define the <=> comparison operator
11   # Here's the comparison operator. We compare 2 student instances based on their scores.
12 
13   def <=>(other)
14     @score <=> other.score
15   end
16  
17 end

  首先,include的module叫Comparable (Java中也有一個同名的接口),即可比較的對象,按照之前對協議、約定的講解,是非常適合使用Mixin模式的。其次,ruby中Comparable這個module也是部分實現,需要具體的類實現<=>方法。

 


總結:

  Mixin是一種思想,用部分實現的接口來實現代碼復用。可以用來解決多繼承的問題,又可以用來擴展功能。Mixin在不同的編程語言中又不同的使用形式或者命名,但其本質都是一樣的。
 
references:
  
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM