Scala设计模式UML图例和代码实现实战 行为模式 之 策略设计模式
策略设计模式
在企业应用程序中,具有不同的特定算法实现并在应用程序运行时选择使用一种算法,这是很常见的事情。一些示例可能包括不同的排序算法,这些算法对于不同大小或类型的数据具有不同的性能,对于各种可能的数据表示具有不同的解析器,等等。
策略设计模式使我们能够定义一系列算法并在运行时选择特定算法。
策略设计模式有助于封装,因为每个算法可以单独定义,然后注入使用它的类中。不同的实现也是可互换的。
示例类图对于类图,让我们假设我们正在编写一个需要从文件加载一些数据然后以某种方式使用这些数据的应用程序。当然,数据可以用不同的格式表示(在这种情况下为CSV或JSON),根据文件类型,我们将使用不同的解析策略。表示我们的解析器的类图如下图所示:我们基本上有一个不同类实现的接口,然后根据需要的接口,为PersonApplication注入正确的接口。
前面的类图看起来非常类似于我们之前在本书中看到的桥梁设计模式。即使是这种情况,两种模式都有不同的目的 - 构建器关注结构,而这里只关注行为。此外,策略设计模式看起来更加耦合。
people.csv 内容:
Ivan,26,London
Maria,23,Edinburgh
John,36,New York
Anna,24,Moscow
people.json 内容:
[
{
"name": "Ivan",
"age": 26,
"address": "London"
},
{
"name": "Maria",
"age": 23,
"address": "Edinburgh"
},
{
"name": "John",
"age": 36,
"address": "New York"
},
{
"name": "Anna",
"age": 24,
"address": "Moscow"
}
]
前面的数据集都包含完全相同的数据,但这些格式使它们看起来完全不同,并且它们在解析时需要不同的方法。
在我们的例子中我们做了一件额外的事情。 我们使用了工厂设计模式,以便在运行时选择正确的实现,具体取决于文件类型:前面的工厂只是一个例子。 它只检查文件扩展名,当然,可以使其更加健壮。 使用这个工厂,我们可以为要使用的应用程序类选择正确的解析器实现,其代码如下所示:无论实现是什么,应用程序类看起来都是一样的。 可以插入不同的实现,只要没有错误,一切都应该运行。
现在,让我们看看我们如何在我们的示例中使用我们的策略设计模式:
case class Person(name: String, age: Int, address: String)
import java.io.InputStreamReader
import com.github.tototoshi.csv.CSVReader
import com.ivan.nikolov.behavioral.strategy.model.Person
import org.json4s._
import org.json4s.jackson.JsonMethods
trait Parser[T] {
def parse(file: String): List[T]
}
class CSVParser extends Parser[Person] {
override def parse(file: String): List[Person] =
CSVReader.open(new InputStreamReader(this.getClass.getResourceAsStream(file))).all().map {
case List(name, age, address) =>
Person(name, age.toInt, address)
}
}
class JsonParser extends Parser[Person] {
implicit val formats = DefaultFormats
override def parse(file: String): List[Person] =
JsonMethods.parse(StreamInput(this.getClass.getResourceAsStream(file))).extract[List[Person]]
}
//具体执行策略的地方, 自动根据文件名后缀执行创建相应的解析器.
object Parser {
def apply(filename: String): Parser[Person] =
filename match {
case f if f.endsWith(".json") => new JsonParser
case f if f.endsWith(".csv") => new CSVParser
case f => throw new RuntimeException(s"Unknown format: $f")
}
}
class PersonApplication[T](parser: Parser[T]) {
def write(file: String): Unit = {
System.out.println(s"Got the following data ${parser.parse(file)}")
}
}
object ParserExample {
def main(args: Array[String]): Unit = {
val csvPeople = Parser("people.csv")
val jsonPeople = Parser("people.json")
val applicationCsv = new PersonApplication(csvPeople)
val applicationJson = new PersonApplication(jsonPeople)
System.out.println("Using the csv: ")
applicationCsv.write("people.csv")
System.out.println("Using the json: ")
applicationJson.write("people.json")
}
}
import java.io.InputStreamReader
import com.github.tototoshi.csv.CSVReader
import com.ivan.nikolov.behavioral.strategy.model.Person
import org.json4s.{StreamInput, DefaultFormats}
import org.json4s.jackson.JsonMethods
class Application[T](strategy: (String) => List[T]) {
def write(file: String): Unit = {
System.out.println(s"Got the following data ${strategy(file)}")
}
}
object StrategyFactory {
implicit val formats = DefaultFormats
def apply(filename: String): (String) => List[Person] =
filename match {
case f if f.endsWith(".json") => parseJson
case f if f.endsWith(".csv") => parseCsv
case f => throw new RuntimeException(s"Unknown format: $f")
}
def parseJson(file: String): List[Person] =
JsonMethods.parse(StreamInput(this.getClass.getResourceAsStream(file))).extract[List[Person]]
def parseCsv(file: String): List[Person] =
CSVReader.open(new InputStreamReader(this.getClass.getResourceAsStream(file))).all().map {
case List(name, age, address) =>
Person(name, age.toInt, address)
}
}
object StrategyExample {
def main(args: Array[String]): Unit = {
val applicationCsv = new Application[Person](StrategyFactory("people.csv"))
val applicationJson = new Application[Person](StrategyFactory("people.json"))
System.out.println("Using the csv: ")
applicationCsv.write("people.csv")
System.out.println("Using the json: ")
applicationJson.write("people.json")
}
}