1

受我对通用策略模式的 C# 实现的启发,我想在 Scala 中做同样的事情。我还想做一些函数式编程来将策略算法封装在继承的类中。所以我所做的是:

trait Strategy {
  type T <: Strategy
  type O

  def Call(victim: T): O = {
    strategy(victim)
  }
  var strategy: (this.T => this.O)
}

这是一个特征,它是烫伤的基础。我也有一StrategyFactory堂课:

case class StrategyFactory[T <: Strategy, O](str: T) {
  def Call(x: (T => O)) = x(str)
}

object StrategyFactory {

}

最后在我的代码中,我可以创建具体的策略:

 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)

但这是非常虚拟的设计,因为我无法创建一组将要扩展的类DownloadStrategy。例如:

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)编译器时,它会非常聪明,可以计算出 willImageDownloadStrategy的子类DownloadStrategy是否可以让我进行一些多态调用,但我做不到。

另一个事实是我需要覆盖type Ttype O在交付的课程中,DownloadStrategy但我不知道该怎么做。

请给我一些建议如何模拟这种行为。

编辑(对于 pagoda_5b 的详细信息)

正如我所提到的,我有功能var strategyin trait Strategywhich is var strategy: (this.T => this.O)。这个变量需要在实现这个特性的类中被覆盖。我还有 2 个泛型类型,这T意味着具体策略的子类,并O指示来自def Call(...).

我想要实现的是在 Strategy 的子类中拥有功能策略,然后进行多态调用。在这里,我得到了DownloadStrategy默认策略,并且我有一些带有特定算法的子类。我想ImageDownloadStrategy转换DownloadStrategy并使用它,就像我在 switch case 语句中展示的那样。

4

1 回答 1

4

好的,我试试拍。

由于您可以拥有函数对象,因此您可能可以简单地不使用任何Strategy层次结构或工厂的机制。

例如,您可以

//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("")
    }

}

如您所见,我缺少从请求传递到策略的输入值是什么,因此该process方法中有几个漏洞

我不知道这是否是您需要的,但它可以让您了解为什么策略模式在函数式语言中不是那么有用,而是不必要的麻烦。

编辑

最后,我有时间在 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
      )
  }
于 2013-07-05T10:02:03.453 回答