2013-12-12 7 views
6

特性Tがあるとします。以下を達成するための最良の方法は何ですか:Tの実装を書き込んScalaでのファクトリの実装を簡潔に行う方法

  • みんながTのパラメータなしの初期化を可能にする可能性を提供することを余儀なくされなければならない、すなわち、我々は、おそらくの実装を強制する必要があります設定可能な工場。
  • 実際の初期化パラメータ(特定の実装AT)にのみ依存するすべてのロジック/データは、一元的に処理/格納する必要がありますが、工場とAの両方で使用可能にする必要があります。

私は(約)これを実現するために見る最も簡単な/簡潔な方法は、この工場に工場やリンクTのための形質を追加するには、次のようになります。

trait T { 
    val factory: TFactory 
} 
trait TFactory { 
    def build(): T 
    val description: String // example for logic/data that only depend on the parameters 
} 

// example implementation: 
class A(val factory: AFactory, paramA: Int, paramB: Int, paramC: Int) extends T 

class AFactory(paramA: Int, paramB: Int, paramC: Int) extends TFactory { 
    def build = new A(this, paramA, paramB, paramC) 
    val description = f"$paramA $paramB $paramC" 
} 

明らかにこれは実際には「強制しません(別の実装が利用可能である限り)ファクトリの実装であり、明らかにAのインスタンス化を生成して、「間違った」TFactoryにリンクすることが可能です。私がこのアプローチについて気に入らないのは、初期化パラメータの繰り返しです。私はしばしば、別のクラスAParamsを作成して、新しいパラメータを追加しやすくするために、すべてのパラメータを再度ラップします。したがって、私は3つのクラスで終わります。imhoは、この単純な問題のための多くの定型文です。

私の質問は、同じ主な目標を達成する(しかし完全に)異なるアプローチがあるかどうかですが、より簡潔ですか?

答えて

1

私はあなたの要件を完全に理解しているとは確信していませんが、この動作についてどう思いますか?

trait TFactory{ 
    def build():T 
    val description:String 
} 

trait T extends TFactory 

//can't declare A without build and not make it abstract 
class A(paramA: Int, paramB: Int, paramC: Int) extends T { 
    def build = new A(paramA, paramB, paramC) 
    val description = f"$paramA $paramB $paramC"  
} 

val a1 = new A(1, 4, 5) 
val a2 = a1.build() 

//We can give ourselves as a factory to something that expects TFactory 
val factory:TFactory = a1 
val a_new = factory.build() 

//More likely we can just give our build method 
def func(f:()=>T) = { 
    val new_t = f() 
    new_t 
} 
val a_newer = func(a1.build) 


println(a1 +": " + a1.description) 
println(a2 +": " + a2.description) 
println(a_new +": " + a_new.description) 
println(a_newer +": " + a_newer.description) 

出力:

[email protected]: 1 4 5 
[email protected]: 1 4 5 
[email protected]: 1 4 5 
[email protected]: 1 4 5 
+0

間違いなく、面白​​いアイデア、ありがとう!私が見る実用的な問題は、工場が軽量であるという概念を直感的に失うことですが、実際には 'T'の実装はかなり重いかもしれません。多くの場合、実際の 'A'の構築には大量の初期化が必要なため、かなりのメモリフットプリントを持つ' A'のインスタンスが生成されます。おそらく、 'A'のインスタンスで終わることはよくあります。これは' T'という実際の意味で実際には使用しませんが、単に不必要なオーバーヘッドを持つファクトリです。しかし、おそらくそれは簡素化のために支払う価格です。 – bluenote10

+0

あなたの工場でTのコンストラクタパラメータを使う必要があるようですので、Tのインスタンスを持たずにそのようなファクトリをどのように持つことができないのか分かりません。ファクトリーとTのサブクラスのコンストラクターの両方に提供できるクラスです。 –

+0

デザインの観点から純粋に言えば、Tを構築する唯一の方法ではない場合、そのようなファクトリを実行する必要はないと思います。おそらく、関数を使うことができるいくつかのメソッドがあります: 'MakeTsAndDoUsefulThings(factory:()=> T)'。 私はあなたのクライアントのコードとして 'SonOfT'を作成し、その関数を使用する必要があることを知りたい場合は、おそらく私のコンパニオンオブジェクトに' SonOfT'のためのファクトリを組み込み、 'MakeTsAndDoUsefulThingsこのメソッドを使用する必要がない場合は、工場を作る必要はありませんが、これは大丈夫です。 –

1

表現型パラメータの追加:

trait Factory[Prod] { 
    def build(): Prod 
} 

trait Prod[Repr] { 
    def factory: Factory[Repr] 
} 

をそれとも、あなたはタイプが同じままであることを "強制" したい場合(私はしないだろうあなたがそれから何かを得ない限り):

trait Prod[Repr <: Prod[Repr]] { 
    def factory: Factory[Repr] 
} 

次に:

case class AConfig(a: Int, b: Int) 

case class A(config: AConfig) extends Prod[A] { 
    def factory = AFactory(config) 
} 

case class AFactory(config: AConfig) extends Factory[A] { 
    def build() = A(config) 
} 

val f0 = AFactory(AConfig(1, 2)) 
val p0 = f0.build() 
val f1 = p0.factory 
val p1 = f1.build() 
assert(p0 == p1) 
関連する問題