2013-12-12 54 views
6

让我们假设我们有一个特质T。什么是实现最好的办法如下:在斯卡拉强制实施工厂的简明方法

  • 大家谁写的T的实现应该被强迫提供一种可能性,即允许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,它再次包装所有参数(例如,以便于添加新参数)。因此,我最终得到了三个类,对于这个简单的问题,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

从设计角度讲purefly如果不是构建T的唯一方法,我不确定你需要执行这样一个工厂。想必你有一些需要一个工厂,它可以是仅仅一个功能的方法:'MakeTsAndDoUsefulThings(工厂:()=> T)'。 然后在未来,如果我是你的代码在客户端做'SonOfT',发现我需要使用的功能,我会为implment'SonOfT'工厂,可能在我的同伴对象,使这个呼叫:'MakeTsAndDoUsefulThings( SonOfT.defaultFactory)' 如果我从来不需要用这个方法,我将永远不需要做工厂,这似乎确定。 –

1

添加一个表示类型参数:

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

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

或者,如果你想 “强制执行” 的类型是一样的(我不会做除非你从中得到一些东西):

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

Then:

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)