程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> Groovy >> Groovy探索之MOP 四 使用ExpandoMetaClass來實現Mixin

Groovy探索之MOP 四 使用ExpandoMetaClass來實現Mixin

編輯:Groovy

國內很多的文章都在說Groovy語言的Mixin機制就是Groovy語言的Categories機制。其實,在外面的Blog上,大量有人在討論Groovy語言應該如何實現它自己的Mixin機制,這就是說明Groovy語言的Mixin機制還沒有定型,處在討論之中。Categories機制當然也能實現部分的Mixin功能,就像Java語言的接口機制,還有組合等等,都能實現部分的Mixin功能。就像C++語言的多繼承一樣,這些"古老"的機制都能或多或少的實現部分的Mixin功能。

其實,Mixin機制也沒有想象的那麼神秘和復雜。從字面上來看,它是由"mix"和"in"兩個單詞組成,它們各自的意義是"混合"和"進來",翻譯過來就是把別的類的功能"混合進來"的意思。從這個意義上講,上面所講到的所有技術,如"多繼承"、"接口"、"組合"和"Categories"都能實現這個機制的部分功能,也就是編譯期的Mixin功能。

但是,現在的Mixin機制所強調的不僅是能夠混入進別的類的功能,更強調的是要在運行期內能夠混入別的類的功能。這就是那些"古老"技術所不能解決的。

在討論中的Groovy語言的Mixin功能,不但要實現編譯期的Mixin功能,同時也要實現運行期的Mixin功能。

雖然Groovy語言的Mixin機制還沒有定型,但由於Groovy語言對MOP的良好支持,所以,我們還是可以比較方便的使用Groovy語言的MOP機制來實現運行期的Mixin功能的。

任何問題的討論都是從一個簡單的例子開始的。

比如,我們現在有一個Window類,它裡面有一個簡單的方法,就是能夠實現開窗的動作。如下:

class Window
{
def open()
{
println 'the window is opened!'
}
}

同時,我們還有一個Human類,這個類可能會很復雜,但為了簡單起見,我們現在不打算實現它的很多功能,只是簡單的表示有這麼一個類。如下:

class Human {

String name

}

現在,我們要操作的當然是Human的實例。本來這兩個類是互不相關的,但現在Human的對象有了一個現實的要求,就是它需要能夠實現"開窗"的動作。

當然,我們可以為Human類實現一個"openWindow"方法,但這一看就不符合面向對象設計的基本原則--類的功能要單一。

接下來的考慮是我們可以在Human類裡使用Window類的"open"方法,這就要使用組合模式,也就實現了編譯期的Mixin機制。但這種編譯期的Mixin機制顯然也太剛性了,可能我們在使用Human類的大多數場合都不會用到"openWindow"方法,卻把它組合到Human類裡。

最後,我們的考慮就是運行期的Mixin機制,這種實現肯定既實現了Mixin的功能,又拋棄了編譯期的Mixin機制的弱點。

現在,我們就來實現運行期的Mixin機制,當然是借助於ExpandoMetaClass類的強大功能。

首先,我們來獲取Window對象:

def window = new Window()

接著,我們要在運行期內借用該對象的"open"方法:

Human.metaClass.openWindow = window.&"open"

現在,我們就可以測試了:

def human = new Human()

human.openWindow()

運行的結果為:

the window is opened!

這就完成了一個簡單的運行期的Mixin功能。

現在,我們的需求可能會在這個基礎上進一步擴展,比如,有一個機器人也希望擁有開窗的功能,或者有一個自動裝置也能開窗。這就需要把我們的Window類的"open"功能Mixin到不同的類中去。

當然,我們可以使用上面的方法,對每一個需要使用開窗功能的類使用ExpandoMetaClass類一一實現。

但既然有這麼多類希望實現相同的功能,我們當然希望有一個工具來幫助我們簡化我們的工作。

class Mixin {

private targetClass

def Mixin(targetClass)
{
this.targetClass = targetClass
}

def mixinWith(String asMethodName, Closure closure)
{
targetClass.metaClass."$asMethodName" = closure
}

}

有了這個Mixin類,我們就可以這樣實現Human類的開窗功能:

def window = new Window()

def m = new Mixin(Human)

m.mixinWith("openWindow",window.&open)

def human = new Human()

human.openWindow()

這個Mixin幫忙類的功能就比較強大了,它可以把其他類的某個方法mixin到Human類去裡;也可以把Window類的"open"方法mixin到其他的某個類中去。

甚至,我們可以直接會Human類添加一個方法,如:

def m = new Mixin(Human)

m.mixinWith('driveCar')
{
println 'The car is running'
}

def human = new Human()

human.driveCar()

運行結果為:

The car is running

當然,你可能對Mixin類的"mixinWith"方法不滿意,希望使用更加DSL的方法,那麼我們就可以這樣實現:

class Mixin {

private targetClass

def Mixin(targetClass)
{
this.targetClass = targetClass
}

def invokeMethod(String name,args)
{
if(name.startsWith('mixinAs'))
{
def methodName = name[7].toLowerCase()+name[8..-1]
targetClass.metaClass."$methodName" = args[0]
}
}


}

然後,我們就可以這樣使用Mixin類:

def window = new Window()

def m = new Mixin(Human)

m.mixinAsOpenWindow window.&open

def human = new Human()

human.openWindow()

上面的代碼行中,形如"m.mixinAsOpenWindow window.&open"就更有可讀性。運行結果同樣為:

the window is opened!

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved