对于几乎所有这些,都有 Scala 替代方案涵盖一些但不是全部这些模式的用例。当然,所有这些都是在我看来,但是:
创作模式
Builder
Scala 使用泛型类型可以比 Java 更优雅地做到这一点,但总体思路是相同的。在 Scala 中,该模式最简单的实现如下:
trait Status
trait Done extends Status
trait Need extends Status
case class Built(a: Int, b: String) {}
class Builder[A <: Status, B <: Status] private () {
private var built = Built(0,"")
def setA(a0: Int) = { built = built.copy(a = a0); this.asInstanceOf[Builder[Done,B]] }
def setB(b0: String) = { built = built.copy(b = b0); this.asInstanceOf[Builder[A,Done]] }
def result(implicit ev: Builder[A,B] <:< Builder[Done,Done]) = built
}
object Builder {
def apply() = new Builder[Need, Need]
}
(如果您在 REPL 中尝试此操作,请确保类和对象生成器定义在同一块中,即使用:paste
.) 检查类型的组合<:<
、泛型类型参数和案例类的复制方法构成了非常强大的组合。
工厂方法(和抽象工厂方法)
工厂方法的主要用途是保持类型的正确性;否则你也可以使用构造函数。借助 Scala 强大的类型系统,您不需要帮助保持类型的正确性,因此您也可以使用构造函数或apply
类的伴随对象中的方法并以这种方式创建事物。特别是在伴生对象的情况下,保持接口一致并不比保持工厂对象中的接口一致更难。因此,工厂对象的大部分动机都消失了。
类似地,抽象工厂方法的许多情况可以通过从适当的特征继承的伴生对象来替换。
原型
当然,重写方法等在 Scala 中占有一席之地。然而,用于原型模式的示例设计模式 http://www.javacamp.org/designPattern/网站在 Scala(或 Java IMO)中是相当不可取的。但是,如果您希望超类根据其子类选择操作而不是让它们自己决定,则应该使用match
而不是笨重的instanceof
tests.
辛格尔顿
Scala 拥抱这些object
。它们是单身人士——使用并享受!
结构模式
Adapter
Scala's trait
在这里提供了更多的功能——例如,您可以创建一个仅实现接口的特征,而不是创建一个实现接口的类part接口的部分,剩下的留给您定义。例如,java.awt.event.MouseMotionListener
需要你填写两个方法:
def mouseDragged(me: java.awt.event.MouseEvent)
def mouseMoved(me: java.awt.event.MouseEvent)
也许您想忽略拖动。然后你写一个trait
:
trait MouseMoveListener extends java.awt.event.MouseMotionListener {
def mouseDragged(me: java.awt.event.MouseEvent) {}
}
现在你只能实现mouseMoved
当你继承这个的时候。所以:类似的模式,但 Scala 的功能更强大。
Bridge
您可以在 Scala 中编写桥接器。它有大量的样板文件,但不像 Java 中那么糟糕。我不建议经常使用它作为抽象方法;首先仔细考虑你的界面。请记住,随着特征功能的增强,您通常可以使用它们来简化更复杂的界面,否则您可能会想编写桥接器。
在某些情况下,您可能希望编写接口转换器而不是 Java 桥接模式。例如,也许您想使用相同的界面来处理鼠标的拖动和移动,仅用布尔标志来区分它们。然后你可以
trait MouseMotioner extends java.awt.event.MouseMotionListener {
def mouseMotion(me: java.awt.event.MouseEvent, drag: Boolean): Unit
def mouseMoved(me: java.awt.event.MouseEvent) { mouseMotion(me, false) }
def mouseDragged(me: java.awt.event.MouseEvent) { mouseMotion(me, true) }
}
这使您可以跳过大部分桥接模式样板,同时实现高度的实现独立性,并且仍然让您的类遵循原始接口(因此您不必不断包装和解开它们)。
合成的
使用案例类来实现复合模式特别容易,尽管进行更新相当困难。它在 Scala 和 Java 中同样有价值。
装饰者
装饰者很尴尬。在继承不完全是您想要的情况下,您通常不想在不同的类上使用相同的方法;你真正想要的是同一个类上的不同方法,它可以完成你想要的事情,而不是默认的事情。这丰富我的图书馆模式 https://stackoverflow.com/questions/11053214通常是一个更好的替代品。
Facade
Facade 在 Scala 中比在 Java 中工作得更好,因为您可以让特征携带部分实现,这样您就不必这样做all当你把它们结合起来时,你自己就可以完成工作。
蝇量级
尽管享元思想在 Scala 中与 Java 中一样有效,但您还可以使用更多工具来实现它:lazy val
,除非实际需要变量,否则不会创建变量(此后会重用),并且by-name parameters
,如果函数实际使用该值,则只需执行创建函数参数所需的工作。也就是说,在某些情况下,Java 模式保持不变。
Proxy
Scala 中的工作方式与 Java 中相同。
行为模式
责任链
在您可以按顺序列出责任方的情况下,您可以
xs.find(_.handleMessage(m))
假设每个人都有一个handleMessage
返回的方法true
如果消息已被处理。如果您想随时改变消息,请使用折叠。
因为很容易让责任方陷入困境Buffer
在某种程度上,Java 解决方案中使用的复杂框架很少在 Scala 中占有一席之地。
Command
这种模式几乎完全被函数所取代。例如,而不是全部
public interface ChangeListener extends EventListener {
void stateChanged(ChangeEvent e)
}
...
void addChangeListener(ChangeListener listener) { ... }
你只需
def onChange(f: ChangeEvent => Unit)
口译员
Scala 提供解析器组合器 http://debasishg.blogspot.com/2008/04/external-dsls-made-easy-with-scala.html它们比作为设计模式建议的简单解释器强大得多。
Iterator
斯卡拉有Iterator
内置于其标准库中。让你自己的类扩展几乎是微不足道的Iterator
or Iterable
;后者通常更好,因为它使重用变得微不足道。绝对是一个好主意,但如此简单,我很难将其称为模式。
Mediator
这在 Scala 中工作得很好,但通常对于可变数据很有用,如果不小心使用,甚至调解器也可能会遇到竞争条件等问题。相反,尽可能尝试将相关数据全部存储在一个不可变的集合、案例类或其他任何内容中,并且在进行需要协调更改的更新时,同时更改所有内容。这不会帮助你与javax.swing
,但在其他方面也广泛适用:
case class Entry(s: String, d: Double, notes: Option[String]) {}
def parse(s0: String, old: Entry) = {
try { old.copy(s = s0, d = s0.toDouble) }
catch { case e: Exception => old }
}
当您需要处理多个不同的关系(每个关系一个调解器)或当您有可变数据时,请保存调解器模式。
Memento
lazy val
对于纪念品模式的许多最简单的应用来说几乎是理想的,例如
class OneRandom {
lazy val value = scala.util.Random.nextInt
}
val r = new OneRandom
r.value // Evaluated here
r.value // Same value returned again
您可能希望专门为惰性求值创建一个小类:
class Lazily[A](a: => A) {
lazy val value = a
}
val r = Lazily(scala.util.Random.nextInt)
// not actually called until/unless we ask for r.value
Observer
这充其量只是一种脆弱的模式。只要有可能,就倾向于保持不可变状态(请参阅调解器),或者使用参与者,其中一个参与者向所有其他参与者发送有关状态更改的消息,但每个参与者都可以应对过时的情况。
State
这在 Scala 中同样有用,并且实际上是应用于无方法特征时创建枚举的首选方法:
sealed trait DayOfWeek
final trait Sunday extends DayOfWeek
...
final trait Saturday extends DayOfWeek
(通常您希望工作日做一些事情来证明这么多样板文件的合理性)。
Strategy
这几乎完全被让方法采用实现策略的函数并提供可供选择的函数所取代。
def printElapsedTime(t: Long, rounding: Double => Long = math.round) {
println(rounding(t*0.001))
}
printElapsedTime(1700, math.floor) // Change strategy
模板法
特征在这里提供了更多的可能性,因此最好将它们视为另一种模式。您可以从抽象级别拥有的尽可能多的信息中填充尽可能多的代码。我真的不想称其为同一件事。
Visitor
Between 结构类型 http://markthomas.info/blog/?p=66 and 隐式转换 http://tomjefferys.blogspot.com/2011/11/implicit-conversions-in-scala.html,Scala 比 Java 的典型访问者模式具有惊人的更多功能。使用原始模式是没有意义的;你只会因为正确的方法而分心。许多例子实际上只是希望在被访问的事物上定义一个函数,Scala 可以轻松地为你做这件事(即将任意方法转换为函数)。