哪些 GOF 设计模式具有完全不同的实现(java 与 Scala)

2024-03-04

最近我读到以下问题:

在 Scala 中是否有使用访问者模式的用例? 我是否应该在每次使用 Scala 时都使用模式匹配 Java 中的访问者模式?

标题问题的链接:Scala 中的访问者模式 https://stackoverflow.com/questions/8618082/visitor-pattern-in-scala。接受的答案开始于

是的,您可能应该从模式匹配开始,而不是 访客模式。看到这个http://www.artima.com/scalazine/articles/pattern_matching.html http://www.artima.com/scalazine/articles/pattern_matching.html

我的问题(受上述问题启发)是哪种 GOF 设计模式在 Scala 中有完全不同的实现?如果我在 Scala 中编程,我应该在哪里小心,不要遵循基于 java 的设计模式(四人帮)编程模型?

创作模式

  • 抽象工厂
  • Builder
  • 工厂方法
  • 原型
  • 单例:直接创建对象(scala)

结构模式

  • Adapter
  • Bridge
  • 合成的
  • 装饰者
  • Facade
  • 蝇量级
  • Proxy

行为模式

  • 责任链
  • Command
  • 口译员
  • Iterator
  • Mediator
  • Memento
  • Observer
  • State
  • Strategy
  • 模板法
  • 访客:模式匹配(scala)

对于几乎所有这些,都有 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 可以轻松地为你做这件事(即将任意方法转换为函数)。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

哪些 GOF 设计模式具有完全不同的实现(java 与 Scala) 的相关文章

随机推荐

  • 在 NumPy 数组中查找重复序列的索引

    这是一个后续上一个问题 https stackoverflow com q 59662725 2955541 如果我有一个 NumPy 数组 0 1 2 2 3 4 2 2 5 5 6 5 5 2 2 对于每个重复序列 从每个索引开始 是否
  • PHP imagejpeg 保存文件不起作用

    我有 php 代码来修剪白色外边框并调整大小 当我使用 imagejpeg newImage 将其输出到浏览器时 它工作正常 但是当我尝试使用 imagejpeg newImage test jpg 保存时 它不会保存在任何地方 请帮助 i
  • 在 Sqlcmd 中转义特殊符号

    我有一个 bat 文件 当我安装应用程序时 它会执行一堆 sql 脚本 在安装对话框中 用户设置数据库名称 登录名和密码 然后在此 bat 文件中使用它们 这里是 SET server local SET db dbname SET use
  • 谷歌云实例在关闭浏览器后终止

    我有一个 bash 脚本 我想在谷歌云服务器上连续运行它 我通过浏览器中的 SSH 连接到我的虚拟机 但关闭浏览器后 脚本停止了 我尝试使用 Cloud Shell 但如果重新启动笔记本电脑 脚本会从头启动 它不能连续工作 是否可以在谷歌云
  • 删除多维数组中的父键

    我想从数组中删除键 Properties 如下所示 我有 Array Values gt 1 List gt Array Product gt Array Details gt Array Properties gt Array Id gt
  • Java中如何设置鼠标的位置?

    我正在使用 Java 进行一些 Swing GUI 工作 我认为我的问题相当简单 如何设置鼠标的位置 正如其他人所说 这可以通过使用来实现Robot mouseMove x y http java sun com javase 6 docs
  • VS2008 PDB 文件大小限制还原:LNK1201 也是

    VS2k8 中 PDB 文件的大小限制是多少 我在一个开发工作站上 32 位 PAE 内核上的 RAM 和页面文件各为 3GB 但在 PDB 增长超过 1GB 后不久 链接器就抛出了 LNK1201 错误 MSPDBSRV 是否只是耗尽了地
  • 正则表达式提取文件名

    我有一个纯文本网络响应 需要提取文件名 对于好的正则表达式有什么建议吗 Total parts 1 Name file Content Type text plain Size 1167 content type text plain co
  • 有没有办法设置 HTML5 范围控件的样式?

    有没有办法设置 HTML5 范围控件的样式 是否可以更改滑块滑动的线条的颜色 事实证明 webkit中有 input type range webkit appearance none important input type range
  • 无法匹配 C 中的正则表达式

    我对以下正则表达式有问题 prefix w w s 0 9 1 4 s s 0 9 1 4 s s 0 9 1 4 s w 匹配字符串如下 prefix string string 100 100 0 string 我无法在我的 C 代码中
  • 多个表上的 FreeText COUNT 查询超级慢

    我有两张桌子 Product ID Name SKU Brand ID Name 产品表大约有120K条记录 品牌表有30K条记录 我需要查找名称和品牌与特定关键字匹配的所有产品的数量 我使用自由文本 包含 如下所示 SELECT coun
  • 验证、方法执行和JIT编译过程中类加载的原因和跟踪

    我试图非常详细地了解哪些事件会导致类加载 并且在测试过程中 我在这个非常基本的示例中遇到了一种我不理解的行为 public class ClinitTest public static Integer num public static L
  • Emacs:是否可以列出 dired 中标记文件的某个查询字符串的所有匹配行?

    前几天我发现 M x 发生了 Emacs中如何实现代码折叠效果 https stackoverflow com questions 1085170 how to achieve code folding effects in emacs 我
  • 转换为 Vector' aria-label='无法将类型 转换为 Vector'> 无法将类型 转换为 Vector

    给定我的 pyspark Row 对象 gt gt gt row Row clicked 0 features SparseVector 7 0 1 0 3 1 0 6 0 752 gt gt gt row clicked 0 gt gt
  • 无法在l5-swagger中生成API文档

    我开始学习招摇了 我正在尝试做书上所做的同样的事情 使用 Angular 6 和 Laravel 5 进行全栈 Web 开发实践 输入命令后使用 php fpm bash php artisan l5 swagger generate 我在
  • jQuery .when().done() 不工作

    首先我想说我是 jQuery 的新手 我怀疑我只是做了一些愚蠢的事情 所以希望这对某人来说会很简单 我正在尝试向我的网站添加滑动移动子菜单 我想要一种手风琴效果 如果我单击一个父链接 它的子子菜单将打开 所有其他子菜单将关闭 问题在于时间
  • Console.ReadLine() 在 VS Code 中不起作用,用 C# 编写代码

    我正在学习 C 并且正在使用 VS Code 当我尝试使用 Console ReadLine 从用户那里获取输入时 它不起作用 我从一个视频中提到了它 它做了完全相同的事情 但经过几次谷歌搜索后 我仍然不知道我哪里出了问题 Console
  • 将 ROW_NUMBER 列添加到流数据帧

    我对 Spark 和 SQL 还很陌生 我正在尝试向我的 df 添加一列 然后将其保存到 Delta 表 该列为每个记录 行提供唯一的 id 并在每次更新特定记录时递增它 我试图执行以下操作 SELECT etc CONCAT somero
  • 类型错误:列表索引必须是整数或切片,而不是列表

    数组 某种具有 3 列和无限数量的行且其中包含数据的列表 Volume array 0 2 counter 0 for i in array if Volume array i 2 lt why is this line a problem
  • 哪些 GOF 设计模式具有完全不同的实现(java 与 Scala)

    最近我读到以下问题 在 Scala 中是否有使用访问者模式的用例 我是否应该在每次使用 Scala 时都使用模式匹配 Java 中的访问者模式 标题问题的链接 Scala 中的访问者模式 https stackoverflow com qu