Interpreter
デザインパターンとは
デザインパターンとはオブジェクト思考開発における先人たちが作り上げてきた便利な設計図です。
Gang of Four通称Gofが1994年に出版した『オブジェクト指向における再利用のためのデザインパターン』の中で23個の設計図が紹介されています。
Note
デザインパターンのサンプルコードはSwift4でまとめます。
インタープリタパターンとは?
Interpreterパターンは構文規則を持った文字列(プログラミング言語)を解析し、得られた命令にしたがって処理を行うパターンです。
身近な例をあげると正規表現などがこのパターンに当てはまります。
サンプルコード
Interpreterパターンでは1つの構文解析を1つのクラスで表現します。サンプルではADDという構文解析を行うクラスを作成し2つの演算対象を足し合わせるクラスを作成しました。カツとカレーとチャーハンを足し合わせています。
Operand(演算対象)
処理対象のItemクラスは文字列を保持してそのまま返すだけのクラスです。
expressionは演算処理を行った結果を出力します。
protocol Operand {
func getOperandString() -> String
}
//処理対象
class Item: Operand {
let item:String
init(item: String) {
self.item = item
}
func getOperandString() -> String {
return item
}
}
//処理結果
class Expression: Operand {
var operate: Operate
init(operate: Operate) {
self.operate = operate
}
func getOperandString() -> String {
return operate.execute().getOperandString()
}
}
構文解析処理
2つの処理クラスを作成しました。1つはMicrowaveクラスですこのクラスはOperand 2文字を追加する処理を持っています。
2つ目はAddクラスです。このクラスでは2つのOperandを足し合わせる処理を持っています。
protocol Operate {
func execute() -> Operand
}
class Microwave: Operate {
private var operate:Operand
init(item: Operand) {
self.operate = item
}
func execute() -> Operand {
return Item(item:"レンジで温めた" + operate.getOperandString())
}
}
class Add: Operate {
private var item1:Operand
private var item2:Operand
init(first: Operand, second: Operand) {
self.item1 = first
self.item2 = second
}
func execute() -> Operand {
return Item(item: item1.getOperandString() + item2.getOperandString())
}
}
呼び出し元
var item0 = Item(item: "カツ")
var item1 = Item(item: "カレー")
var item2 = Item(item: "チャーハン")
//カツカレー
let item3 = Item(item: Expression(operate: Add(first: item0, second: item1)).getOperandString())
//カツカレーチャーハン
let item4 = Item(item: Expression(operate: Add(first: item3, second: item2)).getOperandString())
let microwave = Microwave(item:item4)
let expression = Expression(operate: microwave)
print(expression.getOperandString())
コンソール
レンジで温めたカツカレーチャーハン
コード一覧
import UIKit
protocol Operand {
func getOperandString() -> String
}
//処理対象
class Item: Operand {
let item:String
init(item: String) {
self.item = item
}
func getOperandString() -> String {
return item
}
}
//処理結果
class Expression: Operand {
var operate: Operate
init(operate: Operate) {
self.operate = operate
}
func getOperandString() -> String {
return operate.execute().getOperandString()
}
}
protocol Operate {
func execute() -> Operand
}
class Microwave: Operate {
private var operate:Operand
init(item: Operand) {
self.operate = item
}
func execute() -> Operand {
return Item(item:"レンジで温めた" + operate.getOperandString())
}
}
class Add: Operate {
private var item1:Operand
private var item2:Operand
init(first: Operand, second: Operand) {
self.item1 = first
self.item2 = second
}
func execute() -> Operand {
return Item(item: item1.getOperandString() + item2.getOperandString())
}
}
var item0 = Item(item: "カツ")
var item1 = Item(item: "カレー")
var item2 = Item(item: "チャーハン")
//カツカレー
let item3 = Item(item: Expression(operate: Add(first: item0, second: item1)).getOperandString())
//カツカレーチャーハン
let item4 = Item(item: Expression(operate: Add(first: item3, second: item2)).getOperandString())
let microwave = Microwave(item:item4)
let expression = Expression(operate: microwave)
print(expression.getOperandString())
しめさば日記の例
しめさば日記では文字を一括で入れて分割して解析を行うコードが紹介されていたのでSwift4で書き直してみました。
こっちの例の方がわかりやすいかもしれません。
import UIKit
class Context {
var tokens: [String?] = []
var index = 0
func setTokens(str: String){
for char in str {
var str = ""
str.append(char)
self.tokens.append(str)
}
self.tokens.append(nil)
}
func nextToken() -> String? {
if currentToken() == nil { return nil }
defer{
index += 1
}
return tokens[index]
}
func currentToken() -> String? {
return tokens[index]
}
}
class Expression {
let token: String
init(token: String) {
self.token = token
}
func execute() {
}
}
class TerminalExpression: Expression {
override func execute() {
print("\(token)です")
}
}
class NonterminalExpression: Expression {
let tag = "!"
var expressions = [Expression]()
func parse(context: Context) {
while context.currentToken() != nil {
if (context.currentToken() == tag) {
expressions.append(NonterminalExpression(token: context.currentToken()!))
} else {
expressions.append(TerminalExpression(token: context.currentToken()!))
}
context.nextToken()
}
}
override func execute() {
for expression in expressions {
expression.execute()
}
}
}
let context = Context()
context.setTokens(str: "! A B ! C")
let token = context.currentToken()
context.nextToken()
let expression = NonterminalExpression(token: token!)
expression.parse(context: context)
expression.execute()