Menu
Coddy logo textTech

シングルトンパターン

CoddyのSwiftジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 42/57。

デザインパターンとは、一般的な設計上の問題に対する実証済みの解決策です。シングルトンパターンは、クラスのインスタンスが正確に1つだけ存在し、どこからでもアクセスできるようにします。Swiftでは、インスタンスを保持するstatic letと、別のインスタンスを誰も作成できないようにするprivate initを使います:

final class Settings {
    static let shared = Settings()
    var theme = "light"

    private init() {}
}

Settings.shared.theme = "dark"
print(Settings.shared.theme)

出力:

dark

Settings.sharedを使用するたびに同じオブジェクトを参照するため、プログラムのある部分で行われた変更はどこからでも確認できます。static letは初回使用時に遅延して作成され、複数のスレッドが同時に開始した場合でも安全です:

final class Counter {
    static let shared = Counter()
    private(set) var hits = 0
    private init() {}
    func hit() { hits += 1 }
}

func pageA() { Counter.shared.hit() }
func pageB() { Counter.shared.hit(); Counter.shared.hit() }
pageA()
pageB()
print(Counter.shared.hits)
print(Counter.shared === Counter.shared)

出力:

3
true

イニシャライザが private であるため、Settings() はクラスの外部ではコンパイルできず、「1つだけ」という約束が守られます。

// let other = Settings()   // エラー: 'Settings' イニシャライザは 'private' 保護レベルのためアクセスできません

シングルトンは控えめに使用しましょう。シングルトンはグローバル状態です。どのコードからでも変更でき、テストをクリーンなコピーから簡単に開始することができません。ロガーやアプリの設定など、プログラムごとに本当に1つだけ存在するものには適していますが、オブジェクトをパラメーターとして渡すほうが明確な場合が多くあります。

// テストしやすくするため: ロガーは渡される
func process(order: Order, log: Logger) { log.write("processing \(order.id)") }
challenge icon

チャレンジ

簡単

シングルトン Logger を完成させてください。これには static let shared インスタンス、private イニシャライザ、entries の private(set) 配列、および [INFO] message または [ERROR] message(level は info または error)を保存する log(_:level:) があります。errorCount は計算プロパティです。提供されたコードは2つの異なる関数から Logger.shared を呼び出すため、どちらも同じインスタンスにアクセスしなければなりません。

提供されたコードは info,message または error,message の行を読み取り、info の行を handleInfo に、error の行を handleError に渡し、その後すべてのエントリと errors: 1 を出力します。

コードは Logger.swift と Functions.swift に記述します。main.swift には提供された入出力コードが含まれており、編集できません。

自分で試してみよう

// 提供された入出力コード: そのままにしておく
var input: [String] = []
while let line = readLine() { input.append(line) }
for line in input {
    let p = line.split(separator: ",", maxSplits: 1).map(String.init)
    if p[0] == "error" { handleError(p[1]) } else { handleInfo(p[1]) }
}
for e in Logger.shared.entries { print(e) }
print("errors: \(Logger.shared.errorCount)")
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン

自分で練習してみよう: Swiftプレイグラウンド