ファサードをSwift5で実装する


※この記事は「全デザインパターンをSwift5で実装する」https://qiita.com/satoru_pripara/items/3aa80dab8e80052796c6 の一部です。

The Facade(ファサード)

0. ファサードの意義

機能や使い方等が複雑なコードがある場合に、複雑な実装を隠蔽しシンプルなインターフェースで簡単に扱えるようにするデザインパターンをファサードという(facadeとは、建物の正面や、外見などを意味する言葉)。

1. ファサードを用いない場合

以下は、SwiftのNatural Language Framework(自然言語解析)を用いて、String型の文字列がどの言語で書かれているか判定するコードである。

NLPFacadeDemo.playground
let string = "The Façade is simple yet useful."

let language = NLLanguageRecognizer.dominantLanguage(for: string)

print(language?.rawValue) //en

以下は、同じくString型の文字列内を単語ごとに分割した上で、品詞が何かを判定するコードである。

NLPFacadeDemo.playground

//単語とその品詞をペアにして保持する構造体
public struct WordLexicalClassPair: CustomStringConvertible {
    public var description: String {
        return "\"\(self.word)\": \(self.lexicalClass)"
    }

    let word: String
    let lexicalClass: String
}

let string = "The Façade is simple yet useful"

//タガーを用意する
let tagger = NLTagger(tagSchemes: [.lexicalClass])

var result = [WordLexicalClassPair]()

//判定対象の文章をタガーにセットする
tagger.string = text

//タガーにより文章を品詞ごとに分解し、かつその品詞を判定する。
tagger.enumerateTags(in: text.startIndex..<text.endIndex, unit: NLTokenUnit.word, scheme: NLTagScheme.lexicalClass, options: [.omitPunctuation, .omitWhitespace]) {(tag, range) -> Bool in
            let wordLexicalClass = WordLexicalClassPair(word: String(text[range]), lexicalClass: (tag?.rawValue ?? "unknown"))
            result.append(wordLexicalClass)
            return true
}

print(result)//[ " The ": Determiner, " Façade ": Noun, " is ": Verb, " simple ": Adjective, " yet ": Adverb, " useful ": Adjective] 

上記いずれも、目的を達成するために複雑な手順を踏まなくてはならない上、Natural Language Frameworkに対する理解が不可欠となり扱いづらくなっている。

2. ファサードを用いる場合

ファサード用クラスを用意し、複雑な処理を静的メソッド内に記述する。

NLPFacade.swift
import NaturalLanguage

public struct WordLexicalClassPair: CustomStringConvertible {
    public var description: String {
        return "\"\(self.word)\": \(self.lexicalClass)"
    }

    let word: String
    let lexicalClass: String
}

public class NLPFacade {

    private static let tagger = NLTagger(tagSchemes: [.lexicalClass])

    //言語を判定するメソッド
    public class func dominantLanguage(for string: String) -> String? {
        let language = NLLanguageRecognizer.dominantLanguage(for: string)

        return language?.rawValue
    }

    //文章を単語ごとに分割し、その品詞を判定するメソッド
    public class func partsOfSpeech(for text: String) -> [WordLexicalClassPair]{
        var result = [WordLexicalClassPair]()
        self.tagger.string = text
        self.tagger.enumerateTags(in: text.startIndex..<text.endIndex, unit: NLTokenUnit.word, scheme: NLTagScheme.lexicalClass, options: [.omitPunctuation, .omitWhitespace]) {(tag, range) -> Bool in
            let wordLexicalClass = WordLexicalClassPair(word: String(text[range]), lexicalClass: (tag?.rawValue ?? "unknown"))
            result.append(wordLexicalClass)
            return true
        }
        return result
    }
}

静的メソッドを利用する。Natural Language Frameworkへの理解なしに目的の動作を完了できる。

NLPFacadeDemo.playground
import NaturalLanguage

let text = "The Façade is simple yet useful"
print(text)

let language = NLPFacade.dominantLanguage(for: text) ?? "unknown"
print(language)//en

let result = NLPFacade.partsOfSpeech(for: text)
print(result)//[ " The ": Determiner, " Façade ": Noun, " is ": Verb, " simple ": Adjective, " yet ": Adverb, " useful ": Adjective] 

参考文献: https://www.amazon.com/Design-Patterns-Swift-implement-Improve-ebook/dp/B07MDD3FQJ