Groovy探索 Visitor模式

hivon發表於2009-03-23

                             Groovy探索 Visitor模式

 

 

Groovy語言中的Visitor模式的實現,其實也跟閉包有很大的、直接的關係。當然,你也可以完全不用閉包來實現Visitor模式,就像Java語言那樣來實現。但使用了閉包的Visitor模式更加簡單,更加易於擴充套件。所以,我們在Groovy語言中,傾向於使用閉包來實現Visitor模式。這也是我很久以來,都想把這篇文字放在《Groovy探索 閉包》系列中的原因,但由於Visitor模式的複雜性,使得我傾向於以模式的觀點來說明它,以利於我們的理解。

Java語言實現的Visitor模式一向是難於理解的一個模式,它的實現基本上有兩大類的介面,眾多的實體實現。在這裡我不多說,有興趣的看官或者沒有學過Java語言Visitor模式的,可以在網上找資料來看。

其實,Visitor模式的目的非常簡單,就是把被訪問者的資料結構和訪問者的動作分離,有利於訪問者的擴充套件。這裡,值得注意的是:該模式強調的是被訪問者要有一定的確定性,訪問者才是可以擴充套件的。

在Groovy語言中,我們的訪問者被簡化成一個個的閉包,不再需要一個Visitor介面,讓我們從一個簡單得不能再簡單的例子說起。

比如,我們有如下的一個被訪問者物件:

 

      def visitable = [1,2,3,4,5]

 

 

這是一個簡單的陣列物件,現在,我們的第一個訪問者希望列印出被訪問者的所有元素來。我們就可以這樣來定義一個訪問者:

 

      def visitor1 = {

             println it

      }

 

 

現在,我們讓訪問者去訪問被訪問者:

 

      visitable.each(visitor1)

 

 

執行結果為:

1

2

3

4

5

 

當然,我們使用Visitor模式的目的,就是要擴充套件訪問者,現在,我們就可以擴充套件另外一個訪問者了。

 

      def total = 0

      def visitor2 = {

         total += it

      }

 

 

我們也讓這個訪問者來訪問被訪問者:

 

      visitable.each(visitor2)

     

  println total

 

執行結果為:

15

 

通過上面的例子,我們可以看到,在使用Groovy語言實現的Visitor模式中,我們是如何使用閉包來實現Visitor介面的實現的。正是通過閉包,我們在Groovy語言實現的Visitor模式中,就再也找不到Visitor介面了。

現在,我們再理解Java語言實現的Visitor模式的Visitor介面,其實就是讓訪問者動作有一個實體類可以存在,因為在Java語言中,方法是不能獨立存在的。而在Groovy語言中,方法能夠以閉包的形式獨立存在,所以就用不著Visitor介面了。

理解了這一層關係以後,我們上面的程式碼就可以進一步簡化成如下的樣子了:

 

      visitable.each{

         println it

      }

     

      visitable.each{

         total += it

      }

     

  println total

 

在這裡,我們連宣告一個Visitor物件都省了,但它們實現的也是Visitor模式。

上面是一個最簡單不過的例子,不能顯示出Visitor模式的複雜性來。下面,我們就來說說一個相對複雜一點的例子來,進一步的分析Visitor模式。

這個例子說的是,我們在優惠期間到電腦店去買電腦及周邊產品。電腦店在優惠期間是這樣買商品的:電腦打九折,光碟是買5送1。現在,我們要買一臺電腦,12張光碟,想知道我們花了多少錢。

現在,我們就來模擬這個場景。

首先,我們定義的是被訪問者和訪問者之間的介面類:

 

class Goods {

 

  def accept(Closure doAction)

  {

      doAction(this)

  }

 

}

 

這個Goods類的目的很簡單,就是讓訪問者物件能夠訪問到被訪問者物件,如果沒有這個Goods類,那麼上面的"accept"方法就要分佈在各個被訪問者類中。而"accept"方法中的輸入引數"doAction",就是訪問者物件了。

現在,該我們的Computer類出場了:

 

 

class Computer extends Goods{

 

  def amount

 

  def price

 

  def static DISCOUNT = 0.9

 

  def totalPrice()

  {

      price*amount*DISCOUNT

  }

 

  def totalDiscount()

  {

      price*amount*(1-DISCOUNT)

  }

 

}

 

 

它也很簡單:有兩個輸入屬性分別用來定義購買的數量和單價,一個常量屬性用來定義折扣。兩個方法分別來獲取總價和總的折扣價。

同樣的功能是Disk類,現在由它出場:

 

class Disk extends Goods{

 

  def amount

 

  def price

 

  def totalPrice()

  {

      (amount-(amount/5 as int))*price

  }

 

  def totalDiscount()

  {

      (amount/5 as int)*price

  }

 

}

 

 

它實現的是買五送一的活動。下面的Sales類來定義我們買了多少東西:

class Sales {

   

   

    List goods

   

    def accept(Closure doAction)

    {

       goods.each{

           it.accept(doAction)

       }

    }

 

}

 

 

它的"accept"方法用來使得訪問者能夠訪問我們所買的所有東西。當然,該方法的"doAction"就是我們的訪問者物件了。

現在,一切就緒,我們就可以來訪問被訪問者了。

首先,我們想知道我們總共花了多少錢?

 

      def mySales = new Sales(goods:[new Computer(amount:1,price:500),new Disk(amount:12,price:2)])

     

 

上面的初始化,我們共買了單價為500的電腦一臺,單價為2的光碟12張。我們定義一個變數來存取我們所花的錢:

 

      def totalPrice = 0

 

我們來計算我們總共花了多少錢:

 

      mySales.accept{

         totalPrice += it.totalPrice()

      }

     

      println "my total price is ${totalPrice}"

 

 

結果為:

my total price is 470.0

 

這樣,一個稍微複雜一點的Visitor模式就全部實現了。

你可能要問了,我的訪問者物件不是可以擴充套件嗎?你擴充套件一個給我看看,好,我們現在來計算我們總共省了多少錢。

 

      def totalDiscount = 0

     

      mySales.accept{

         totalDiscount += it.totalDiscount()

      }

     

      println "my total discount is ${totalDiscount}"

   

 

執行結果為:

my total discount is 54.0

 

使用閉包實現的Visitor模式還有一個好處是能夠擴充套件被訪問者,使用在Java語言中實現的Visitor所難以做到的,如果要做到,一般要用到反射技術。而在Groovy語言閉包所實現的Visitor中,我們可以很輕鬆的擴充套件被訪問者。

比如,我們現在還想買一個滑鼠,它沒有折扣,如下:

 

class Mouse extends Goods{

 

  def amount

 

  def price

 

  def totalPrice()

  {

      amount*price

  }

 

  def totalDiscount()

  {

      0

  }

 

}

 

現在,我們的客戶端就可以這樣使用:

 

      def mySales = new Sales(goods:[new Computer(amount:1,price:500),new Disk(amount:12,price:2),

                                     new Mouse(amount:1,price:10)])

     

     

      def totalPrice = 0

     

      mySales.accept{

         totalPrice += it.totalPrice()

      }

     

      println "my total price is ${totalPrice}"

     

      def totalDiscount = 0

     

      mySales.accept{

         totalDiscount += it.totalDiscount()

      }

     

      println "my total discount is ${totalDiscount}"

   

 

執行結果為:

my total price is 480.0

my total discount is 54.0

 

可以看出,在Groovy語言中使用閉包實現的Visitor是非常好理解的,簡單到你沒有意識到你是在使用Visitor模式,而且,這個Visitor模式也很容易擴充套件它的被訪問者。就像上面的例子那樣。

相關文章