2012-07-18 56 views
3

我有以下類層次結構。皮條客我的庫模式應用於類層次結構

sealed abstract class A 
case class B extends A 
case class C extends A 

現在,我想一個foo方法添加到類ABC。但我不想以任何方式改變課程。所以我應用皮條客我的圖案模式如下。

abstract class RichA(a: A) { 
    def foo 
} 
class RichB(b: B) extends RichA(b){ 
    def foo = { println("B"); //do something with b} 
} 
class RichC(c: C) extends RichA(c) { 
    def foo = { println("C"); //do something with c} 
} 
///////////// 
implicit def A2RichA(a: A) = { 
    a match { 
    case a: B => new RichB(a) 
    case a: C => new RichC(a) 
    } 
} 
implicit def B2RichB(b: B) = new RichB(b) 
implicit def C2RichC(c: C) = new RichC(c) 
///////////// 
def test() = { 
    def printA(a: A) = { 
     a.foo 
    } 
    val obj = new C 
    printA(obj) 
} 
test() //This prints "C" 

這是工作,但A2RichA隱函數的實現看上去有點難看,我因爲它涉及到case語句爲每個A的子類都可以在這個更優雅的方式做?基本要求是,如果我在A類型的對象上調用foo,則應調用BC中的適當方法foo,具體取決於對象的動態類型。

+0

你想同''foo''添加到您的課程,或者你需要爲每個''A,B,C''的不同的實現? – 2012-07-18 08:41:08

回答

0

第一種方法: - 根據OP不足 -

這樣做,假設一個實現foo服中所有類的層次結構:

sealed abstract class A 
case class B() extends A 
case class C() extends A 

class RichA(a: A) { 
    def foo() { println(this.a.getClass.getSimpleName) } 
} 

implicit def a2RichA(a: A): RichA = new RichA(a) 

(new C).foo() /* anon$1$C */ 


二方法: - 缺乏動態調度 -

我的第二種方法是受到Scala collection library的內部啓發。但是,它缺乏動態調度,所以它仍然不能解決您的問題。

sealed abstract class A 
case class B() extends A 
case class C() extends A 

abstract class RichA(a: A) { 
    def foo(): Unit 
} 

class RichB(b: B) extends RichA(b) { 
    def foo() { println("Doing B-specific stuff") } 
} 

class RichC(c: C) extends RichA(c) { 
    def foo() { println("Doing C-specific stuff") } 
} 

sealed trait RichBuilder[T <: A] { 
    def apply(t: T): RichA 
} 

implicit object RichB extends RichBuilder[B] { 
    def apply(b: B) = new RichB(b) 
} 

implicit object RichC extends RichBuilder[C] { 
    def apply(c: C) = new RichC(c) 
} 

implicit def a2RichA[T <: A](t: T) 
          (implicit rb: RichBuilder[T]) 
          : RichA = { 

    rb(t) 
} 

(new B).foo() /* B-specific */ 
(new C).foo() /* C-specific */ 
// (new C).asInstanceOf[A].foo() /* ERROR: Can't find an implicit */ 


聲明:

如果你想要的行爲依賴於運行時類型的對象,有兩種可能性,我可以看到:

  1. 模式匹配
  2. 動態派遣

在您的原始代碼中使用的模式匹配似乎會導致您在執行匹配的代碼中有一個位置的情況。因此,類層次結構的擴展需要對該地點進行更改 - 這可能是不可能的,例如,如果層次結構由客戶端擴展。

動態調度不會受到這個缺點,這可能是爲什麼這種方法在Scala集合中使用的原因(在文章中簡要提及)。因此,您可以嘗試使用雙重調度(參見visitor pattern)。但是,這有一個缺點,即基類A必須已經聲明瞭相應的方法 - 這有點失敗了Pimp My Library Pattern的目標。斯卡拉2.10s新Dynamic功能可能在這裏有所幫助,但由於缺少經驗,我不能對此發表評論。

+0

很抱歉,如果我簡化了問題太多much..The類'B'和'C'可以有不同的數據和foo'的'實施是'B'和'C'不同。 – dips 2012-07-18 08:51:49

+0

由於我的方法並沒有真正解決問題 - 每個人都請駁回我的觀點。 – 2012-07-18 09:45:50

+0

感謝您的指點..它需要我一些時間來消化。 – dips 2012-07-18 10:06:04

1

您可以讓編譯器提供正確的轉換讓你A2RichA方法稍微更優雅(或高深莫測的,取決於你的觀點):

implicit def A2RichA(a: A): RichA = 
    a match { 
     case b: B => b // the compiler turns this into B2RichB(b) 
     case c: C => c // the compiler turns this into C2RichC(c) 
    } 

implicit def B2RichB(b: B): RichA = new RichB(b) 

implicit def C2RichC(c: C): RichA = new RichC(c) 

不過,我不認爲有一個簡單的方法以解決基本問題:您希望提供基於動態類型的參數的轉換。隱式搜索在編譯時發生,因此只能提供基於靜態類型的轉換。

你可以沉思搜索在運行時轉換,但這將是既不簡單也不優雅(當然不可取,這樣一個小的層次結構)。

由於您的層次結構是密封的,編譯器警告您,如果您在向層次結構添加新類時忘記提供轉換。