1. 程式人生 > >Scala設計模式UML圖例和程式碼實現實戰 行為模式--模板方法設計模式

Scala設計模式UML圖例和程式碼實現實戰 行為模式--模板方法設計模式

模板方法設計模式  示例類圖

模板方法設計模式適用於實現框架。這裡典型的是演算法通常執行相同的步驟集,然後這些步驟由不同的客戶端以不同的方式實現。您可以提出各種可能的用例。 對於我們的示例,讓我們假設我們要編寫一個應用程式,它將從資料來源中讀取一些資料,解析它,並查詢是否存在滿足某些條件的物件並將其返回。如果我們考慮它,我們有以下主要操作:讀取資料解析資料搜尋滿足條件的專案如果需要,清理所有資源下圖顯示了我們程式碼的類圖:我們使用了一個示例,我們'已經顯示了從檔案中讀取有關人​​員的資料。然而,在這裡,我們使用它來查詢滿足過濾功能的人的資料。使用模板方法設計模式,我們可以從伺服器,資料庫或任何想到的東西中讀取具有不同格式的檔案的人員列表。使用多型,我們的應用程式確保呼叫正確的方法,一切正常。程式碼示例讓我們通過代表上圖的程式碼,看看它的作用。

case class Person(name: String, age: Int, address: String)
import java.io.{InputStreamReader, ByteArrayInputStream}

import com.github.tototoshi.csv.CSVReader
import com.ivan.nikolov.behavioral.template.model.Person
import org.json4s.{StringInput, DefaultFormats}
import org.json4s.jackson.JsonMethods

abstract class DataFinder[T, Y] {

  def find(f: T => Option[Y]): Option[Y] =
    try {
      val data = readData()
      val parsed = parse(data)
      f(parsed)
    } finally {
      cleanup()
    }

  def readData(): Array[Byte]

  def parse(data: Array[Byte]): T

  def cleanup()
}

class JsonDataFinder extends DataFinder[List[Person], Person] {
  implicit val formats = DefaultFormats

  override def readData(): Array[Byte] = {
    val stream = this.getClass.getResourceAsStream("people.json")
    Stream.continually(stream.read).takeWhile(_ != -1).map(_.toByte).toArray
  }

  override def cleanup(): Unit = {
    System.out.println("Reading json: nothing to do.")
  }

  override def parse(data: Array[Byte]): List[Person] =
    JsonMethods.parse(StringInput(new String(data, "UTF-8"))).extract[List[Person]]
}

class CSVDataFinder extends DataFinder[List[Person], Person] {
  override def readData(): Array[Byte] = {
    val stream = this.getClass.getResourceAsStream("people.csv")
    Stream.continually(stream.read).takeWhile(_ != -1).map(_.toByte).toArray
  }

  override def cleanup(): Unit = {
    System.out.println("Reading csv: nothing to do.")
  }

  override def parse(data: Array[Byte]): List[Person] =
    CSVReader.open(new InputStreamReader(new ByteArrayInputStream(data))).all().map {
      case List(name, age, address) =>
        Person(name, age.toInt, address)
    }
}


object DataFinderExample {
  def main(args: Array[String]): Unit = {
    val jsonDataFinder: DataFinder[List[Person], Person] = new JsonDataFinder
    val csvDataFinder: DataFinder[List[Person], Person] = new CSVDataFinder

    System.out.println(s"Find a person with name Ivan in the json: ${jsonDataFinder.find(_.find(_.name == "Ivan"))}")
    System.out.println(s"Find a person with name James in the json: ${jsonDataFinder.find(_.find(_.name == "James"))}")

    System.out.println(s"Find a person with name Maria in the csv: ${csvDataFinder.find(_.find(_.name == "Maria"))}")
    System.out.println(s"Find a person with name Alice in the csv: ${csvDataFinder.find(_.find(_.name == "Alice"))}")
  }
}

執行結果如下:

首先,我們的模型Person類:case類Person(name:String,age:Int,address:String)沒有什麼特別之處。現在,讓我們轉到有趣的部分 - DataFinder類:我們使用泛型來使這個類可用於各種型別。正如您在前面的程式碼中看到的那樣,DataFinder類的三個方法沒有實現,但它們仍然在find方法中引用。後者是實際的模板方法,抽象方法將在擴充套件DataFinder的不同類中實現。 對於我們的示例,我們提供了兩種不同的實現,一種用於JSON,另一種用於CSV檔案。 JSON查詢器看起來如下:每當我們使用它時,根據我們具有的具體例項,find方法將通過多型呼叫正確的實現。可以通過擴充套件DataFinder類來新增新格式和資料來源。 使用我們的資料查詢器現在很簡單:我們示例中的程式碼使用抽象類。這使得它在某種意義上略有限制,即我們只能擴充套件一個類。 但是,將抽象類更改為特徵然後將其混合到類中會很簡單。 它有什麼好處正如您所看到的,每當我們有一個演算法結構相同且我們提供不同實現的用例時,我們就可以使用模板方法設計模式。這非常適合建立框架。 它不是那麼好用每當我們使用模板方法設計模式實現的框架變大時,簡單地擴充套件一個龐大的類並實現它的一些方法就更難了。在這些情況下,將介面傳遞給建構函式並在框架中使用它可能是一個更好的想法(策略設計模式)。