Swiftでデザインパターン シングルトンパターン


シングルトンデザインパターン

  • 一番有名なデザインパターンなのではないでしょうか。
  • 普通、クラスはインスタンス化するごとに、別のインスタンスになるが、シングルトンパターンを使うと、インスタンスが常にひとつのものを利用されることを保証されます
  • Xcode上でも、iOS開発の際にUserDefaultsやURLSessionインスタンスにアクセスすることができますが、それらはシングルトンであり、どこからアクセスしても同じインスタンスが返ってきます
  • 賛否両論のパターンだと思われます
  • 新人だったら先輩に利用して良いか聞くべきかもしれません(あとで設計に関して怒られるかも)

メリット

  • メモリを節約できる
  • 別の画面でグローバルに使いたい場合
  • インスタンスを一つしか使わせたくない場合
  • データの不整合や、ファイルアクセスなどをするようなクラスを利用する場合は、毎回インスタンス化していると時間がかかるため使える

デメリットとアンチパターン

  • シングルトンのインスタンスの状態は管理しづらい(どこで状態が変わるかわからないため)
    • シングルトンは状態を持たず、副作用を持たないようにしたほうがよい
    • 状態を持たないほうが、テストも簡単
  • ファイルアクセスなどを専有したりすると、障害の元になる
  • テストしづらい
  • アプリ終了時にシングルトンが開放されるが、複数あった場合、開放のタイミングが不明
  • ただのグローバル変数になっているものもある
  • スレッドセーフ(複数のスレッドで同時に実行しても安全)であるような設計になってない(他の場所で同じインスタンスにアクセスすると落ちるとか障害の元になるときがある)

グローバル変数との違い

  • グローバル変数としてだけ実装していると、別にインスタンス化できてしまう

class GlobalVariable {}
let global = GlobalVariable()
let global2 = GlobalVariable()
// 一つのインスタンスとしての利用が保証されていない!
// 名前空間を汚染してしまう

実装方法のポイント2つ

  • 他のクラスからインスタンス化できないこと
  • アクセスするメソッドが提供されていること

class FileAccessManager {

    private static let sharedInstance: FileAccessManager = {
        return FileAccessManager(filePath: FileAccessManager.initialFilePath)
    }()

    private static let initialFilePath: String = "var/www/path"
    private let filePath: String

    private init(filePath: String) {
        self.filePath = filePath
    }

    class func shared() -> FileAccessManager {
        return self.sharedInstance
    }

    func fetchContent() {
        print(self.filePath)
    }
}

let manager = FileAccessManager.shared()
manager.fetchContent()

// 私見ですが、あらゆるところで使うというよりは、ある特定の場所でしか使わないほうが良いと思います