Ok, I'll try to take a shot.
Since you can have function objects, you probably can simply do without any of the machinery of a Strategy
hierarchy or factory whatsoever.
You can for example
//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("")
}
}
As you can see, I'm missing what is the input value passed from the request to the strategy, so there are a couple holes in the process
method
I don't know if this is what you need, but it could give you an idea why the strategy pattern is not so useful in functional languages, but rather needlessly cumbersome.
EDIT
Finally I found time to post real life example of downloading strategy in 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
)
}