如何在Scala中实施类似策略模式的内容

时间:2018-10-18 01:18:39

标签: scala

我想在scala中实现类似于策略模式的某种功能,而无需诉诸与一长串case语句匹配的模式。这大致就是我的想法:

trait HandlerTrait {
   def handlerA(...): Unit
   def handlerB(...): Unit
}

SomeClass1 extends HandlerTrait {
   override def handlerA(...) {...}
   override def handlerB(...) {...}
}

SomeClass2 extends HandlerTrait {
   override def handlerA(...) {...}
   override def handlerB(...) {...}
}

object MyApp extends App {
   // 1. define bindings for these implementations
   val myBindings = Map(x -> someClass1, y -> someClass2)

   // 2. Such that implementation of someMethod targeting handlerA implementations could look like this:
   def someMethod(object: ObjectType): Unit = {
      myBindings.get(object.x) match {
        case Some(entry) => entry.handlerA(object)
        case None => ()
      }
   }
}

还有几件事:

  1. 我不知道我会拥有几个SomeClassXXX。我将根据需要添加 提供有关如何处理A / B / C的自定义...
  2. 给出一个键,我想分派到正确的类并执行目标处理程序。

在scala中,有没有更好,更简洁的方法来实现这一目标?

1 个答案:

答案 0 :(得分:0)

我认为减少样板的一种方法是使用类型系统而不是继承。例如,如果您的处理程序的类型为<nav class="navbar"> <div id="ABT-Container"> <ul id="nav"> <li class="w3-animate-right" id="menuElement"> <a href="#" onclick="toggle(this)"> <p class="menu_line"></p> <p class="menu_line"></p> <p class="menu_line"></p> </a> <ul> <li> <a href="#" onclick="console.log('about us clicked')">About US</a> <ul> <li></li> </ul> </li> <li> <a href="#" onclick="toggle(this)">Multi-Levels</a> <ul> <li> <a href="#" onclick="toggle(this)">Sales</a> <ul> <li> <a href="#" onclick="console.log('support clicked')">Support</a> </li> </ul> </li> <li> <a href="#" onclick="console.log('another link clicked')">Another Link</a> </li> <li> <a href="#" onclick="console.log('department clicked')">Department</a> </li> </ul> </li> <li> <a href="#" onclick="toggle(this)">Multi-Levels2</a> <ul> <li> <a href="#" onclick="toggle(this)">Sales2</a> <ul> <li> <a href="#" onclick="console.log('support2 clicked')">Support2</a> </li> </ul> </li> <li> <a href="#" onclick="toggle(this)">Sales3</a> <ul> <li> <a href="#" onclick="console.log('support clicked3')">Support3</a> </li> </ul> </li> <li> <a href="#" onclick="console.log('another link2 clicked')">Another Link2</a> </li> <li> <a href="#" onclick="console.log('department clicked2')">Department2</a> </li> </ul> </li> </ul> </li> </ul> </div> </nav>,那么满足此类型的任何函数都可以是处理程序,则无需正式声明T => Unit甚至HandlerTrait

是否使用someMethodMap从键到处理程序的映射取决于您。两者都可以扩展以处理新案件。

以下是一个总结我提议的例子:

case

和用法:

val currentlyDefinedStrategies: PartialFunction[String, Unit] = {
  case "1" => println(1)
  case "2" => println(2)
}

val newStrategies: PartialFunction[String, Unit] = {
  case "3" => println(3)
}

val defaultStrategy: PartialFunction[String, Unit] = {
  case _ => println("default")
}

您可以使用scala> currentlyDefinedStrategies("1") 1 scala> currentlyDefinedStrategies("3") scala.MatchError: 3 (of class java.lang.String) ... scala> currentlyDefinedStrategies.orElse(newStrategies)("3") 3 scala> currentlyDefinedStrategies.orElse(newStrategies)("4") scala.MatchError: 4 (of class java.lang.String) scala> currentlyDefinedStrategies.orElse(newStrategies).orElse(defaultStrategy)("4") default 或使用其他FP技术来实现类似的模式。重点是保留最相关的代码并摆脱样板。当然,Map对于您构造代码和根据类而不是函数进行思考可能会很有用,但是思想是相同的。

另请参阅:https://pavelfatin.com/design-patterns-in-scala/#strategy

上面的示例有点简化,您实际上想将参数传递给处理程序(在我们的例子中为HandlerTrait)。方法如下:

println

您可以在不选择策略的情况下解决问题:

val currentlyDefinedStrategies: Int => PartialFunction[String, Unit] = (x) => { 
  case "1" => println("1: " + x)
  case "2" => println("2: " + x)
  case _ => println("default: " + x)
}

...并随后提供策略:

scala> val noStrategy = currentlyDefinedStrategies(1)
noStrategy: PartialFunction[String,Unit] = <function1>

或立即应用该策略:

scala> noStrategy("1")
1: 1

您还可以先决定策略,然后再传递参数:

scala> currentlyDefinedStrategies(1)("1")
1: 1

我认为关键是FP是如此丰富和灵活,以至于策略模式真的不是问题。基本上,val currentlyDefinedStrategies: PartialFunction[String, Int => Unit] = { case "1" => x => println("1: " + x) case "2" => x => println("2: " + x) case _ => x => println("default: " + x) } scala> val handlerWithChosenStrategy = currentlyDefinedStrategies("1") handlerWithChosenStrategy: Int => Unit = $anonfun$1$$Lambda$1374/666224848@59a9f3eb scala> handlerWithChosenStrategy(1) 1: 1 就是一些适合您使用的函数类型。示例:

type Strategy[T, -A, +B] = PartialFunction[T, A => B]

一个更高级的概念是表达式问题(here),您需要在其中扩展您可以对类型执行的操作以及添加新类型。