通过使用 Trait 和 with 关键字来减少代码
我有一些具有相同超类型的课程。因此,所有此类都必须重写相同的方法。现在我可以调用一个方法并将其提交为公共超类型的对象。但对每个提交的类型做出反应并不总是有用,因此会引发异常。首先,我尝试像这样解决此行为:
def operation(s: SuperType) = s match {
case t: SubType1 => ...
case t: SubType2 => ...
case _ => ...
}
由于有很多子类型,这将导致大量代码(在每个方法和每个类中),我尝试用 traits
。每个特征应该只测试一种类型,然后将对象转发到堆栈上的更高层方法。下面的代码描述了我的想象。但这不起作用,因为编译器无法解析类型。另一个问题是我必须在每个行为类中声明类的每个属性。
object TraitWithTest {
def main(args: Array[String]) {
val e1 = Even(2, 4)
val e2 = Even(1, 3)
val o1 = Odd(1.25, 3.75)
val o2 = Odd(7.25, 9.25)
val a1 = All(5.5)
val a2 = All(3.5)
println("e1 + e2: " + (e1 + e2))
println("o1 + o2: " + (o1 + o2))
try { println("e1 + o2: " + (e1 + o2)) } catch { case e => println(e) }
println("o1 + e2: " + (o1 + e2))
println("a1 + e1: " + (a1 + e2))
}
}
abstract class Num {
def +(n: Num): Num
}
trait OddBehaviour extends Num {
val e1, e2: Int // here I don't want to declare all attributes
val a1: Double
abstract override def +(n: Num) = n match {
case o: Odd => throw new UnsupportedOperationException("Even#+(Odd)")
case _ => super.+(n)
}
}
trait EvenBehaviour extends Num {
val o1, o2: Double
val a1: Double
abstract override def +(n: Num) = n match {
case e: Even => Odd(o1 + e.e1, o2 + e.e2)
case _ => super.+(n)
}
}
trait AllBehaviour extends Num {
val o1, o2: Double
val e1, e2: Int
abstract override def +(n: Num) = n match {
case a: All => Odd(o1 + a.a1, o2 + a.a1)
case _ => super.+(n)
}
}
object Even {
def apply(e1: Int, e2: Int) = new Even(e1, e2) with OddBehaviour with AllBehaviour
}
abstract case class Even(e1: Int, e2: Int) extends Num {
override def +(n: Num) = n match {
case c: Even => Even(e1 + c.e1, e2 + c.e2)
case _ => throw new IllegalArgumentException
}
}
object Odd {
def apply(o1: Double, o2: Double) = new Odd(o1, o2) with EvenBehaviour with AllBehaviour
}
abstract case class Odd(o1: Double, o2: Double) extends Num {
override def +(n: Num) = n match {
case o: Odd => Odd(o1 + o.o1, o2 + o.o2)
case _ => throw new IllegalArgumentException
}
}
object All {
def apply(a1: Double) = new All(a1) with EvenBehaviour with OddBehaviour
}
abstract case class All(a1: Double) extends Num {
override def +(n: Num) = n match {
case a: All => All(a1 + a.a1)
case _ => throw new IllegalArgumentException
}
}
有人可以告诉我是否可以通过使用特征来减少代码行数?或者我目前使用的全匹配解决方案是最好的吗?
编辑:
在你的帮助下,我找到了一个半有效的解决方案。我的主要问题是我试图通过使用 Scala 功能来减少代码行数。所以我忽略了最简单的方法:外包代码!我只需要创建一个新对象来检查对象组合。对象本身只处理它们自己的类型。
这是代码:
final object TraitWithTest {
def main(args: Array[String]) {
import traitwith.operations._
val e1 = Even(2, 4)
val e2 = Even(1, 3)
val o1 = Odd(1.25, 3.75)
val o2 = Odd(7.25, 9.25)
val a1 = All(5.5)
val a2 = All(3.5)
val n1 = NumHolder(o1)
val n2 = NumHolder(a1)
println("e1 + e2: " + add(e1, e2))
println("o1 + o2: " + add(o1, o2))
try { println("e1 + o2: " + add(e1, o2)) } catch { case e => println(e) }
println("o1 + e2: " + add(o1, e2))
try { println("a1 + e2: " + add(a1, e2)) } catch { case e => println(e) }
println("n1 + n2: " + add(n1, n2))
}
}
final object operations {
def add(a: Num, b: Num) = a -> b match {
case (a1: Odd, b1: Odd) => a1 + b1
case (a1: Odd, b1: Even) => Odd(a1.x + b1.x, a1.y + b1.y)
case (a1: Odd, b1: All) => Odd(a1.x + b1.x, a1.y + b1.x)
case (a1: Even, b1: Even) => a1 + b1
case (a1: All, b1: All) => a1 + b1
case _ => error("can't add " + b + " to " + a)
}
}
abstract class Num {
type A <: Num
def +(a: A): A
}
final case class Odd(x: Double, y: Double) extends Num {
override type A = Odd
override def +(a: Odd) = Odd(x + a.x, y + a.y)
}
final case class Even(x: Int, y: Int) extends Num {
override type A = Even
override def +(a: Even) = Even(x + a.x, y + a.y)
}
final case class All(x: Double) extends Num {
override type A = All
override def +(a: All) = All(x + a.x)
}
final case class NumHolder(x: Num) extends Num {
override type A = NumHolder
override def +(a: NumHolder) = NumHolder(x + a.x)
}
我对代码进行了一些扩展并插入了对象 NumHolder
。现在,只有一个小缺陷:在 NumHolder 中,我无法在添加方法中不出现编译错误的情况下提交超类型。我尝试使用泛型而不是类型关键字,但这很不方便,因为我总是将类型设置为 Num(也在对象操作中)。
我该如何解决这个小编译错误?
I have some classes with the same super-type. Therefore all of this classes have to override the same methods. Now I can call a method and commit it an object of the common super-type. But it is not always useful to react to each committed type therefore an exception is thrown. First i tried to solve this behaviour like this:
def operation(s: SuperType) = s match {
case t: SubType1 => ...
case t: SubType2 => ...
case _ => ...
}
Due to a lot of sub-types this will result to a lot of code (in each method and in each class) and I tried to solve this problem with traits
. Each trait should test only one type and then forward the object to the higher method on the stack. The code below describes how I imagine that. But this don't work because the compiler can't dissolve the types. An other problem is that I have to declare each attribute of the classes in each behaviour class.
object TraitWithTest {
def main(args: Array[String]) {
val e1 = Even(2, 4)
val e2 = Even(1, 3)
val o1 = Odd(1.25, 3.75)
val o2 = Odd(7.25, 9.25)
val a1 = All(5.5)
val a2 = All(3.5)
println("e1 + e2: " + (e1 + e2))
println("o1 + o2: " + (o1 + o2))
try { println("e1 + o2: " + (e1 + o2)) } catch { case e => println(e) }
println("o1 + e2: " + (o1 + e2))
println("a1 + e1: " + (a1 + e2))
}
}
abstract class Num {
def +(n: Num): Num
}
trait OddBehaviour extends Num {
val e1, e2: Int // here I don't want to declare all attributes
val a1: Double
abstract override def +(n: Num) = n match {
case o: Odd => throw new UnsupportedOperationException("Even#+(Odd)")
case _ => super.+(n)
}
}
trait EvenBehaviour extends Num {
val o1, o2: Double
val a1: Double
abstract override def +(n: Num) = n match {
case e: Even => Odd(o1 + e.e1, o2 + e.e2)
case _ => super.+(n)
}
}
trait AllBehaviour extends Num {
val o1, o2: Double
val e1, e2: Int
abstract override def +(n: Num) = n match {
case a: All => Odd(o1 + a.a1, o2 + a.a1)
case _ => super.+(n)
}
}
object Even {
def apply(e1: Int, e2: Int) = new Even(e1, e2) with OddBehaviour with AllBehaviour
}
abstract case class Even(e1: Int, e2: Int) extends Num {
override def +(n: Num) = n match {
case c: Even => Even(e1 + c.e1, e2 + c.e2)
case _ => throw new IllegalArgumentException
}
}
object Odd {
def apply(o1: Double, o2: Double) = new Odd(o1, o2) with EvenBehaviour with AllBehaviour
}
abstract case class Odd(o1: Double, o2: Double) extends Num {
override def +(n: Num) = n match {
case o: Odd => Odd(o1 + o.o1, o2 + o.o2)
case _ => throw new IllegalArgumentException
}
}
object All {
def apply(a1: Double) = new All(a1) with EvenBehaviour with OddBehaviour
}
abstract case class All(a1: Double) extends Num {
override def +(n: Num) = n match {
case a: All => All(a1 + a.a1)
case _ => throw new IllegalArgumentException
}
}
Can someone give say me whether it is possible to reduce lines of code by using traits? Or is the match-all-solution I currently using the best?
EDIT:
With your help I found a half-working-solution. My main-problem was that I tried to reduce the lines of code by using Scala-features. So I overlooked the easiest way: outsourcing the code! I only have to create a new object which checks the object-combinations. The objects themselves handle only their own types.
This is the code:
final object TraitWithTest {
def main(args: Array[String]) {
import traitwith.operations._
val e1 = Even(2, 4)
val e2 = Even(1, 3)
val o1 = Odd(1.25, 3.75)
val o2 = Odd(7.25, 9.25)
val a1 = All(5.5)
val a2 = All(3.5)
val n1 = NumHolder(o1)
val n2 = NumHolder(a1)
println("e1 + e2: " + add(e1, e2))
println("o1 + o2: " + add(o1, o2))
try { println("e1 + o2: " + add(e1, o2)) } catch { case e => println(e) }
println("o1 + e2: " + add(o1, e2))
try { println("a1 + e2: " + add(a1, e2)) } catch { case e => println(e) }
println("n1 + n2: " + add(n1, n2))
}
}
final object operations {
def add(a: Num, b: Num) = a -> b match {
case (a1: Odd, b1: Odd) => a1 + b1
case (a1: Odd, b1: Even) => Odd(a1.x + b1.x, a1.y + b1.y)
case (a1: Odd, b1: All) => Odd(a1.x + b1.x, a1.y + b1.x)
case (a1: Even, b1: Even) => a1 + b1
case (a1: All, b1: All) => a1 + b1
case _ => error("can't add " + b + " to " + a)
}
}
abstract class Num {
type A <: Num
def +(a: A): A
}
final case class Odd(x: Double, y: Double) extends Num {
override type A = Odd
override def +(a: Odd) = Odd(x + a.x, y + a.y)
}
final case class Even(x: Int, y: Int) extends Num {
override type A = Even
override def +(a: Even) = Even(x + a.x, y + a.y)
}
final case class All(x: Double) extends Num {
override type A = All
override def +(a: All) = All(x + a.x)
}
final case class NumHolder(x: Num) extends Num {
override type A = NumHolder
override def +(a: NumHolder) = NumHolder(x + a.x)
}
I extended the code a bit and inserted the object NumHolder
. Now, there is only one little flaw: In NumHolder I can't commit the super-type without getting a compile error in the add-method. I tried to use Generics instead of the type-keyword but that is unhandy because I have always to set a type to Num (also in the object operations).
How can I solve this little compile error?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
您的问题是您正在尝试使用面向对象的功能(例如类和继承)以及非面向对象的设计。
OOP 的全部要点是您无需内省类是什么。相反,使用多态性来实现结果。我特别喜欢这篇论文来说明面向对象应该如何工作,但在这方面并不缺乏资源。
编辑
例如,提供的代码大致翻译为以下内容,减去不起作用的内容(由于它们,提供的代码无法准确编译)。
在我看来,它可以通过访问者模式进一步改进,这是有道理的,因为它针对的问题与类型匹配通常遇到的问题相同。
Your problem is that you are trying to use object oriented features, such as classes and inheritance, with a design that is not object oriented.
The whole point of OOP is that you do not introspect what a class is. Use, instead, polymorphism to achieve the results. I particularly like this paper in illustrating how OO is supposed to work, but there are no shortage of resources out there in this respect.
EDIT
For example, the code provided roughly translates into the following, minus the things that don't work (the code provided doesn't compile precisely because of them).
It looks to me it can be further improved with the visitor pattern, which makes sense, given that it targets the same problems that type matching usually does.
错误引用某个广告:有一个类型类...
Scala 已经通过
Numeric
支持“数字”的临时多态性,这可能是您真正想要的: http:// www.scala-lang.org/archives/downloads/distrib/files/nightly/docs/library/scala/math/Numeric.html但是如果这个 Even/Odd/All 方案是你实际上正在做的,而不仅仅是一个人为的示例,那么您可以随时推出自己的类型类!
我们称之为
Addable
:免责声明:我还没有实际测试代码,这台机器还没有安装Scala
To misquote a certain advert: There's a type class for that...
Scala already supports ad-hoc polymorphism over "numbers" via
Numeric
, which is probably what you really want: http://www.scala-lang.org/archives/downloads/distrib/files/nightly/docs/library/scala/math/Numeric.htmlBut if this Even/Odd/All scheme is what you're actually doing, and not just a contrived example, then you can always roll your own type class!
Let's call it
Addable
:disclaimer: I haven't actually tested the code, this machine doesn't (yet) have Scala installed
另一种解决方案,适用于直到运行时才知道类型的情况:
这里的技巧是首先将两个参数包装到一个元组中,这样您就可以在一个对象上进行模式匹配。
更新
进行编辑后;您似乎在任何地方都不需要抽象类型
A
,为什么不将Num
保留为标记特征并在每个子类中单独定义 + 方法?An alternative solution, for when types aren't known until runtime:
The trick here is to first wrap both params into a tuple, so you then have a single object to pattern-match on.
UPDATE
Following your edit; you don't seem to need the abstract type
A
anywhere, why not just leaveNum
as a marker trait and define the + method separately in each subclass?我不知道是否可以解决您的问题,但在思考时,我尝试至少让您的示例编译并工作,希望这至少有一定帮助。
我以
toString
方法的形式添加了一些噪音,以便能够查看实例化和表达式的结果。“最后的手段”类,
All
,应该是一个案例类,以使匹配更顺利,但由于它将作为真正的案例类继承,因此效果不佳。具有apply
和unapply
方法的伴随对象可以解决这个问题。All
可以处理类似的术语,但不会尝试处理Even
和Odd
术语,因为这些术语是秘密的All。
现在,
Even
和Odd
的工作方式可以被提炼为特征,但对于本例来说不是必需的。不这样做会简化继承,但可能违背示例的要点,我不知道。Even
知道如何处理Even
和Odd
项,但将任何其他项传递给其超类。同样,它是一个用于匹配目的的人造案例类。Odd
知道如何处理Even
术语,但拒绝处理Odd
术语(我从你的示例中更改了这一点以制作一个弱双关语,不客气)。好的,让我们来试一下。
I don't know if I can solve your problem, but while thinking about it I tried to at least get your example to compile and work, which is hopefully at least somewhat helpful.
I've added some noise in the form of
toString
methods to be able to see the results of instantiations and expressions.The "last resort" class,
All
, should be a case class to make the matching smoother, but since it's going to be inherited as true case class won't work well. A companion object withapply
andunapply
methods solves that.An
All
can handle similar terms, but doesn't try to handleEven
andOdd
terms since those terms are secretlyAll
s at the same time.Now, the way
Even
s andOdd
s work could be distilled into traits, but it isn't necessary for this example. Not doing so simplifies inheritance, but possibly goes against the point of the example, I don't know.An
Even
knows how to handleEven
andOdd
terms, but passes any others to its superclass. Again, it's a faux case class for matching purposes.An
Odd
knows how to handleEven
terms, but refuses to deal withOdd
ones (I changed this from your example to make a weak pun, you're welcome).OK, let's take it for a spin.
看来泛型可能对你有帮助。尝试这样的事情:
你的问题描述不是很清楚,所以这是一个猜测......
It looks like generics might help you. Try something like this:
Your problem description isn't very clear, so this is a guess...
基于 Kevin Wright 的答案我现在解决了问题:
我在超级类型中不再有一些方法 - 我希望,同一天,这不会引起问题。可以删除类中的所有添加方法,但在我的实际应用程序中,我有更大的类,所以我需要它们。
based on Kevin Wright's answer I solved the problem now:
I don't have any more some methods in the super-type - I hope, same day, this will not cause problems. It is possible to delete all the add-methods in the classes but in my real application I have larger classes so I need them there.