Factory对象创建后:如何调用仅在某些情况下定义的方法?

时间:2016-10-13 12:11:02

标签: scala design-patterns

我有一个类(我现在叫它run #1: random 1: 0 random 2: 1 random 3: 8 random 4: 5 random 5: 5 run #2 random 1: 0 random 2: 2 random 3: 15 random 4: 9 random 5: 11 run #3 random 1: 0 random 2: 8 random 3: 15 random 4: 28 random 5: 2 run #4 random 1: 0 random 2: 16 random 3: 10 random 4: 15 random 5: 24 run #5 random 1: 0 random 2: 26 random 3: 1 random 4: 42 random 5: 6 )可以有不同的特征:它必须是A1或A2(但不是两者),它必须是B1或B2(但不是两者)。随着这些特点来到不同的领域和不同的功能。我决定通过将A1到B2作为特征实现并根据需要将它们混合到实例化中来对此进行建模。确定在实例化期间我想要哪种对象是棘手的,所以我选择了Factory Pattern,特别是MyClass的伴随对象(MyClass的构造函数是私有的)。但是如果我使用MyClass作为工厂方法的返回类型,如

MyClass

当我与

实例化时,我无法使用A1或A2中的方法
def create(...parameters...): MyClass {
    if(...parameters' complex calculations...) {
        new MyClass(...) with A1
    } else {
        new MyClass(...) with A2
    }
 }

这并不奇怪,因为a的类型是 val a = MyClass.create(...parameters...) a.methodFromA1 // not available 而不是MyClassMyClass with A1在运行时确定,而不是在编译时确定,因此可用的方法将在运行时确定,这对于类型安全的语言是不可能的。

我该如何解决这个问题?

我的一个想法是以某种方式使用密封特征并将特征A1,A2,B1,B2转换为案例类。毕竟我可能需要稍后使用某种模式匹配。在我的情况下,只有四种可能的组合(...parameters...MyClass with A1 with B1MyClass with A1 with B2MyClass with A2 with B1)这甚至可行,但如果我只有一对特征,这个会变得相当丑陋很快。我无法弄清楚细节。

我希望有一种设计模式 - 我不应该是第一个遇到这个问题的人。在https://pavelfatin.com/design-patterns-in-scala/#factory-method中,假设MyClass with A2 with B2工厂可以返回AnimalDog,具体取决于字符串输入。如果您认为Cat有一个Dog方法,我们希望在创建后将其用作客户端,但bark没有 - 那种情况应该与我的非常相似。猫不会发出任何噪音(无论出于何种原因),所以没有“咆哮猫咪”。

1 个答案:

答案 0 :(得分:0)

“如果你想象一只狗有一种我们希望在创建后用作客户端的树皮方法,但是Cat没有”,那么工厂方法就不合适了。它的先决条件是客户端只想使用通用接口。

你可以尝试做的是参数化MyClass而不是混合物:

sealed trait A { ... }
trait A1 extends A { ... }
trait A2 extends A { ... }

// similarly for B

class MyClass[TA <: A, TB <: B] { 
  val a: TA = ... // or it could be a parameter of the private constructor
  val b: TB = ...
}
object MyClass {
  def create(...parameters...): MyClass[...] = ...
}

create的返回类型的参数必须依赖于参数的类型,而不是依赖于值。 (Scala确实根据值支持特定类型的类型,但在您的情况下这不太可能有用。)

使用您的设计,可以模式匹配:

val a = MyClass.create(...parameters...)
a match {
  case a1: A1 => 
    a1.methodFromA1
  case _ =>
    // do nothing
}

或在methodFromA1返回值的更有用的情况下:

val a = MyClass.create(...parameters...)
val x = a match {
  case a1: A1 => 
    Some(a1.methodFromA1)
  case _ =>
    None
}

但通过这样做,您放弃了Factory Method的一些优点。

当然,这并不意味着这总是错误的解决方案,但通常更好的是扩展界面:

trait A { 
  def foo(): ReturnType
}

trait A1 {
  def foo() = methodFromA1
}

trait A2 {
  def foo() = aDifferentMethod(someParameters)
}