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()

参考文献

Interpreter パターン

Swiftで学ぶデザインパターン23 (Interpreterパターン)

矢沢久雄の早わかりGoFデザインパターン(最終回)

IT専科 Interpreter パターン

デザインパターン

前の記事

Decorator
デザインパターン

次の記事

Adapter