我有一个类(我现在叫它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
而不是MyClass
。 MyClass with A1
在运行时确定,而不是在编译时确定,因此可用的方法将在运行时确定,这对于类型安全的语言是不可能的。
我该如何解决这个问题?
我的一个想法是以某种方式使用密封特征并将特征A1,A2,B1,B2转换为案例类。毕竟我可能需要稍后使用某种模式匹配。在我的情况下,只有四种可能的组合(...parameters...
,MyClass with A1 with B1
,MyClass with A1 with B2
,MyClass with A2 with B1
)这甚至可行,但如果我只有一对特征,这个会变得相当丑陋很快。我无法弄清楚细节。
我希望有一种设计模式 - 我不应该是第一个遇到这个问题的人。在https://pavelfatin.com/design-patterns-in-scala/#factory-method中,假设MyClass with A2 with B2
工厂可以返回Animal
或Dog
,具体取决于字符串输入。如果您认为Cat
有一个Dog
方法,我们希望在创建后将其用作客户端,但bark
没有 - 那种情况应该与我的非常相似。猫不会发出任何噪音(无论出于何种原因),所以没有“咆哮猫咪”。
答案 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)
}