Scala策略模式设计问题
受我对通用策略模式的C#实现的启发,我想在Scala中也这样做。我还想做一些函数式编程,将策略算法封装在继承的类中。 所以我现在做的是:Scala策略模式设计问题,scala,design-patterns,strategy-pattern,Scala,Design Patterns,Strategy Pattern,受我对通用策略模式的C#实现的启发,我想在Scala中也这样做。我还想做一些函数式编程,将策略算法封装在继承的类中。 所以我现在做的是: trait Strategy { type T <: Strategy type O def Call(victim: T): O = { strategy(victim) } var strategy: (this.T => this.O) } 最后,在我的代码中,我可以创建具体的策略: class Downlo
trait Strategy {
type T <: Strategy
type O
def Call(victim: T): O = {
strategy(victim)
}
var strategy: (this.T => this.O)
}
最后,在我的代码中,我可以创建具体的策略:
class DownloadStrategy(path: String) extends Strategy {
type T = DownloadStrategy
type O = String
strategy = (dw: DownloadStrategy) => path + "aaaa"
}
object DownloadStrategy {
def apply(s: String) = new DownloadStrategy(s)
}
在我的应用程序代码中,我有:
var ds = DownloadStrategy("j")
val m = StrategyFactory[DownloadStrategy, String](ds)
var output = m.Call(ds.strategy)
这里一切都很好。
我想要功能策略,因此有m.Call(ds.strategy)
但这是一个非常虚假的设计,因为我无法创建一组将扩展下载策略的类。例如:
class ImageDownloadStrategy(w: String, h: String, path: String) extends DownloadStrategy(path){
type T = ImageDownloadStrategy
type O = String
strategy = (ids: T) => path + ":ImageDownloadStrategy"
}
class VideoDownloadStrategy(w: String, h: String, path: String) extends DownloadStrategy(path){
type T = VideoDownloadStrategy
type O = String
strategy = (ids: T) => path + ":VideoDownloadStrategy"
}
等等。基本上,我希望有一些默认策略的一个基类,子类是更具体的实现
这就引出了应用程序代码,我想在其中编写如下代码:
var ds: DownloadStrategy = null
request.getQueryString("t") match {
case "1" => ds = ImageDownloadStrategy("","","")
case "2" => ds = VideoDownloadStrategy("","","")
case "3" => ds = RawFileDownloadStrategy("","","")
case _ => ds = DownloadStrategy("")
}
var output = (StrategyFactory[DownloadStrategy, String](ds)).Call(ds.strategy)
我认为当我编写StrategyFactory[DownloadStrategy,String](ds)
时,编译器将非常智能,可以判断ImageDownloadStrategy
是否是DownloadStrategy
的子类,它将允许我执行一些polymorphic调用,但我无法执行
另一个事实是,我需要在DownloadStrategy
提供的类中重写typet
和typeo
,但我不知道如何做
请给我一些如何塑造这种行为的建议
编辑(用于宝塔5b详细信息)
正如我所提到的,我在trait-strategy
中有功能var-strategy
,它是var-strategy:this.O)
。这个变量需要在实现这个特性的类中被重写。我还有两个泛型类型,T
表示具体策略的子类,O
表示来自def调用(…)
的结果类型
我想要实现的是在Strategy的子类中有函数策略,然后进行polimographic调用。这里我有DownloadStrategy
,这是默认策略,我有一些带有特殊算法的子类。我想将ImageDownloadStrategy
转换为DownloadStrategy
,并像我在switch case语句中所显示的那样使用它。好的,我将尝试拍摄
由于您可以拥有函数对象,因此可能不需要策略的任何机制
层次结构或工厂
你可以举个例子
//this is sort of a factory
object Strategies {
//a type alias to better define your selected functions
type Strategy[T, O] = T => O
//a set of methods to obtain the correct strategy "on demand"
def imageDownload[T](w: String, h: String, path: String): Strategy[T, String] =
(t: T) =>
path + ":ImageDownloadStrategy"
def videoDownload[T](w: String, h: String, path: String): Strategy[T, String] =
(t: T) =>
path + ":VideoDownloadStrategy"
def rawFileDownload[T](w: String, h: String, path: String): Strategy[T, String] =
(t: T) =>
path + ":RawDownloadStrategy"
//this is the fallback default
def download[T](path: String): Strategy[T, String] =
(t: T) =>
path + "aaaa"
}
object Client {
//make the strategies visible
import Strategies._
//processes the request
def process(request: Request): String = {
//here val means that the strategy variable won't be reassigned, ever
val strategy = selectStrategy[T](request.getQueryString("t")) //here we miss the type of the input value
//this assignment could be omitted if it's just returned
val output = strategy(??) //Here I'm missing the input to the strategy
output
}
//a method to select the strategy to use
def selectStrategy[T](selector: String): Strategy[T, String] =
selector match {
case "1" => imageDownload("","","")
case "2" => videoDownload("","","")
case "3" => rawFileDownload("","","")
case _ => download("")
}
}
如您所见,我缺少从请求传递到策略的输入值,因此过程中有几个漏洞
我不知道这是否是您所需要的,但它可以让您了解为什么策略模式在函数式语言中不是那么有用,而是相当麻烦
编辑
最后,我抽出时间发布playframework中下载策略的真实示例
object Download{
object Type extends Enumeration {
type Type = Value
val Image = "1"
val Video = "2"
val Pdf = "3"
val File = "4"
}
}
object Strategies {
type Strategy[T, O] = T => O
def imageDownload[T](): Strategy[T, java.io.File] =
(t: T) => {
//Receive download strategy information
val dw = t.asInstanceOf[DownloadStrategy]
//juicy code goes here
java.io.File.createTempFile("", "")
}
def videoDownload[T](): Strategy[T, java.io.File] =
(t: T) =>
java.io.File.createTempFile("", "")
def rawFileDownload[T](): Strategy[T, java.io.File] =
(t: T) =>
java.io.File.createTempFile("", "")
//this is the fallback default
def download[T](): Strategy[T, java.io.File] =
(t: T) => {
java.io.File.createTempFile("", "")
}
//a method to select the strategy to use
def selectStrategy[T](selector: String): Strategy[T, java.io.File] =
selector match {
case Download.Type.Image => {
imageDownload()
}
case Download.Type.Video => {
videoDownload()
}
case Download.Type.Pdf => {
rawFileDownload()
}
case Download.Type.File => {
rawFileDownload()
}
case _ => download()
}
}
case class DownloadStrategy(request: Request[AnyContent], path: String, file: Option[File]) {
}
//Controller code
def download(path: String) = Action {
implicit request =>
val file: Option[File] = FileStore.byPath(path, true)
val ds = DownloadStrategy(request, path, file)
//request.getQueryString("t") - Download type
val str = Strategies.selectStrategy[DownloadStrategy](request.getQueryString("t").getOrElse(""))
val x = str(ds)
Ok.sendFile(
content = x
)
}
好的,我试试看
由于您可以拥有函数对象,因此可能不需要策略的任何机制
层次结构或工厂
你可以举个例子
//this is sort of a factory
object Strategies {
//a type alias to better define your selected functions
type Strategy[T, O] = T => O
//a set of methods to obtain the correct strategy "on demand"
def imageDownload[T](w: String, h: String, path: String): Strategy[T, String] =
(t: T) =>
path + ":ImageDownloadStrategy"
def videoDownload[T](w: String, h: String, path: String): Strategy[T, String] =
(t: T) =>
path + ":VideoDownloadStrategy"
def rawFileDownload[T](w: String, h: String, path: String): Strategy[T, String] =
(t: T) =>
path + ":RawDownloadStrategy"
//this is the fallback default
def download[T](path: String): Strategy[T, String] =
(t: T) =>
path + "aaaa"
}
object Client {
//make the strategies visible
import Strategies._
//processes the request
def process(request: Request): String = {
//here val means that the strategy variable won't be reassigned, ever
val strategy = selectStrategy[T](request.getQueryString("t")) //here we miss the type of the input value
//this assignment could be omitted if it's just returned
val output = strategy(??) //Here I'm missing the input to the strategy
output
}
//a method to select the strategy to use
def selectStrategy[T](selector: String): Strategy[T, String] =
selector match {
case "1" => imageDownload("","","")
case "2" => videoDownload("","","")
case "3" => rawFileDownload("","","")
case _ => download("")
}
}
如您所见,我缺少从请求传递到策略的输入值,因此过程中有几个漏洞
我不知道这是否是您所需要的,但它可以让您了解为什么策略模式在函数式语言中不是那么有用,而是相当麻烦
编辑
最后,我抽出时间发布playframework中下载策略的真实示例
object Download{
object Type extends Enumeration {
type Type = Value
val Image = "1"
val Video = "2"
val Pdf = "3"
val File = "4"
}
}
object Strategies {
type Strategy[T, O] = T => O
def imageDownload[T](): Strategy[T, java.io.File] =
(t: T) => {
//Receive download strategy information
val dw = t.asInstanceOf[DownloadStrategy]
//juicy code goes here
java.io.File.createTempFile("", "")
}
def videoDownload[T](): Strategy[T, java.io.File] =
(t: T) =>
java.io.File.createTempFile("", "")
def rawFileDownload[T](): Strategy[T, java.io.File] =
(t: T) =>
java.io.File.createTempFile("", "")
//this is the fallback default
def download[T](): Strategy[T, java.io.File] =
(t: T) => {
java.io.File.createTempFile("", "")
}
//a method to select the strategy to use
def selectStrategy[T](selector: String): Strategy[T, java.io.File] =
selector match {
case Download.Type.Image => {
imageDownload()
}
case Download.Type.Video => {
videoDownload()
}
case Download.Type.Pdf => {
rawFileDownload()
}
case Download.Type.File => {
rawFileDownload()
}
case _ => download()
}
}
case class DownloadStrategy(request: Request[AnyContent], path: String, file: Option[File]) {
}
//Controller code
def download(path: String) = Action {
implicit request =>
val file: Option[File] = FileStore.byPath(path, true)
val ds = DownloadStrategy(request, path, file)
//request.getQueryString("t") - Download type
val str = Strategies.selectStrategy[DownloadStrategy](request.getQueryString("t").getOrElse(""))
val x = str(ds)
Ok.sendFile(
content = x
)
}
所有这些工作都可以通过使用函数作为值来轻松替换,这是函数编程的最大好处之一,也是scala的最大好处之一。如果您不详细说明单个策略应该如何使用其构造参数(即w、h、path),我就无法写出完整的答案。strategy
成员是可变重要的吗?如果是这样的话,从功能角度来看,我不喜欢这种模式。如果不是,它只是一个函数T=>O
,不是吗?@ziggystar我不知道可变是什么意思?意思是多变?我同意策略可以是val,但需要在子用例中重写或实现。这里的可变意味着使用var
而不是val
,这使得策略
特征可变。您的子类仍然可以覆盖val
。两者之间的区别在于不可变值不能重新分配,有点像final
或constant
。所有这些工作都可以通过使用函数作为值来轻松替代,这是函数编程的一大好处,也是scala的一大好处。如果您不详细说明单个策略应该如何使用其构造参数(即w、h、path),我就无法写出完整的答案。strategy
成员是可变重要的吗?如果是这样的话,从功能角度来看,我不喜欢这种模式。如果不是,它只是一个函数T=>O
,不是吗?@ziggystar我不知道可变是什么意思?意思是多变?我同意策略可以是val,但需要在子用例中重写或实现。这里的可变意味着使用var
而不是val
,这使得策略
特征可变。您的子类仍然可以覆盖val
。两者之间的区别在于不可变值不能重新分配,有点像final
或constant
。