Scala設計模式UML圖例和程式碼實現實戰 行為模式--直譯器設計模式
Scala直譯器設計模式
在現代程式設計中,我們有時必須處理來自易於理解和定義明確的域的問題。在某些情況下,使用語言表示域是有意義的,這樣可以使用直譯器輕鬆解決問題。 直譯器設計模式對於指定如何使用類來表示語句並構建語法樹來評估語言表示式非常有用。 直譯器設計模式也使用複合設計模式。直譯器的一些常見用途是語言解析,協議等。 示例類圖建立語言和語法是一項複雜的任務,在進入它之前,開發人員應該確信它確實值得付出努力。它需要非常好地理解正在建模的域,並且通常需要一些時間。在本節中,我們將介紹有關程式的直譯器部分的類圖,該程式以反向波蘭表示法解析和評估表示式。這是電腦科學中的一個重要概念,它顯示了計算機在執行不同操作時的實際工作方式。螢幕截圖如下所示:我們語言的主要概念是Expression。一切都是表達,正在被解釋。 我們可以在圖中區分兩種主要型別的表示式:終端表示式:這是Number類。在構建表示式的語法樹時,它是一個終端,它沒有其他子節點(葉節點)。 非終結表示式:這些是Add,Subtract和Multiply類。它們具有子表示式,這就是整個語法樹的構建方式。 上面的螢幕截圖僅顯示直譯器將轉換我們的語言的那些表示式。在下面的小節中,我們將另外顯示可以使這樣的應用程式工作的所有其他類。 程式碼示例在這裡,我們將逐步顯示直譯器應用程式的程式碼。我們目前有一些限制,例如只支援整數,沒有良好的錯誤報告機制,只有三個操作,但很容易新增新的操作。你可以嘗試在我們已有的基礎上進行構建。
##首先,讓我們看一下基本的表達特徵
特徵表示式{def interpret():Int} 它非常簡單,並且包含一個其他表示式必須實現的方法。終結表示式,我們的Number類,如下所示: class Number(n:Int)extends Expression {override def interpret():Int = n} 它沒有做任何特殊的事情 - 只返回它攜帶的數字解釋被稱為。非終結表示式有更多的程式碼,但它們都非常簡單:
類新增(右:表示式,左:表示式)擴充套件表示式{覆蓋def解釋():
Int = left.interpret()right.interpret()} c lass Subtract(右:Expression,left:Expression)extends Expression {override def interpret():Int = left.interpret() - right.interpret()} class Multiply(right:Expression,left:Expression)extends Expression {override def interpret():Int = left.interpret()* right.interpret()}
到目前為止,這是我們在圖中顯示的所有內容,它是直譯器設計模式的基本部分。有些人可能會注意到建構函式中的任何地方,我們首先使用右手錶達式,然後是左手錶達式。這是有目的地完成的,因為當我們實際實現我們的解析器時,它會使程式碼更加清晰。 從現在開始,我們將展示如何在實際應用程式中解析和使用設計模式。首先,我們需要建立一個基於令牌的工廠,該工廠決定應該返回哪個表示式: object Expression {def apply(operator:String,left:=> Expression,right:=> Expression):Option [Expression ] = operator match {case“”=> Some(new Add(right,left))case“ - ”=> Some(new Subtract(right,left))case“*”=> Some(new Multiply(right,left) ))case i if i.matches(“\ d”)=> Some(new Number(i.toInt))case _ => None}}
在前面的程式碼中,我們應用了一些技術和設計模式已經通過工廠和名稱引數。後者非常重要,因為基於這種情況,我們的程式碼命中決定了它們是否會被評估。
trait Expression { def interpret(): Int }
class Add(right: Expression, left: Expression) extends Expression { override def interpret(): Int = left.interpret() + right.interpret() }
class Subtract(right: Expression, left: Expression) extends Expression { override def interpret(): Int = left.interpret() - right.interpret() }
class Multiply(right: Expression, left: Expression) extends Expression { override def interpret(): Int = left.interpret() * right.interpret() }
class Number(n: Int) extends Expression { override def interpret(): Int = n }
object Expression { def apply(operator: String, left: => Expression, right: => Expression): Option[Expression] = operator match { case “+” => Some(new Add(right, left)) case “-” => Some(new Subtract(right, left)) case “*” => Some(new Multiply(right, left)) case i if i.matches("\d+") => Some(new Number(i.toInt)) case _ => None } }
import java.util.StringTokenizer
import scala.collection.JavaConverters._ import scala.collection.mutable
class RPNParser {
def parse(expression: String): Expression = { val tokenizer = new StringTokenizer(expression) tokenizer.asScala.foldLeft(mutable.StackExpression) { case (result, token) => val item = Expression(token.toString, result.pop(), result.pop()) item.foreach(result.push) result }.pop() } }
class RPNInterpreter { def interpret(expression: Expression): Int = expression.interpret() }
object RPNExample { def main(args: Array[String]): Unit = { val expr1 = “1 2 + 3 * 9 10 + -” // (1 + 2) * 3 - (9 + 10) = -10 val expr2 = “1 2 3 4 5 * * - +” // 1 + 2 - 3 * 4 * 5 = -57 val expr3 = “12 -” // invalid val parser = new RPNParser val interpreter = new RPNInterpreter
System.out.println(s"The result of '${expr1}' is: ${interpreter.interpret(parser.parse(expr1))}")
System.out.println(s"The result of '${expr2}' is: ${interpreter.interpret(parser.parse(expr2))}")
try {
System.out.println(s"The result is: ${interpreter.interpret(parser.parse(expr3))}")
} catch {
case _: Throwable => System.out.println(s"'$expr3' is invalid.")
}
} }