Upgrade to Pro — share decks privately, control downloads, hide ads and more …

アプリケーションデザイン_5.pdf

k-kohey
July 19, 2019
110

 アプリケーションデザイン_5.pdf

k-kohey

July 19, 2019
Tweet

Transcript

  1. "QQMJDBUJPO%FTJHO ษڧձ

    ஜ೾େֶ ৘ใϝσΟΞ૑੒ֶྨ೥
    ઒ޱ ߤฏ
    1

    View Slide

  2. 4JOHMFUPOύλʔϯͱ
    .POPTUBUFύλʔϯ
    2

    View Slide

  3. ໰୊
    • プログラムが開始した際に⽣成され,終了する際に処分すべきインスタンスがある.
    • オブジェクトの⽣成を⾏うFactoryのような存在.
    • オブジェクトを管理する存在.
    • このような存在が複製⽣成されると論理破綻を起こすといった問題が発⽣する.
    例)どのインスタンスに対しても同⼀の結果を期待するが,実際は参照するインスタンスによって結果
    が異なってしまう.
    3

    View Slide

  4. ໨త
    • プログラムが開始した際に⽣成され,終了する際に処分すべきインスタンスがある.
    • オブジェクトの⽣成を⾏うFactoryのような存在.
    • オブジェクトを管理する存在.
    • このような存在が複製⽣成されると論理破綻を起こす.
    例)どのインスタンスに対しても同⼀の結果を期待するが,実際は参照するインスタンスによって結果
    が異なってしまう.
    4
    4JOHMFUPO.POPTUBUFύλʔϯΛ༻͍ͯ
    Πϯεελϯεͷ།ҰੑΛอূ͢Δ

    View Slide

  5. final class SingltonClass {
    private static var shared: SingltonClass? = nil
    static func instance() -> SingltonClass {
    if let shared = self.shared {
    return shared
    }
    return .init()
    }
    }
    4JOHMFUPOύλʔϯ
    • 特徴
    • PublicかつStaticなメソッドを通じてシステム上において唯⼀のインスタンスを取得する.
    • Publicなコンストラクタを持たない.
    5

    View Slide

  6. 4JOHMFUPOύλʔϯΛ༻͍ΔϝϦοτٴͼσϝϦοτ
    • クロスプラットフォーム
    • RMIなどを⽤いて,他のJVMに共有できる.
    • あらゆるクラスに適応可能
    • コントラスタをprivateにして共有インスタンスを返すメソッドを⽣
    やせば良い
    • 派⽣を使って⽣成することも可能
    • ⾃分を返すのではなくて,⼦クラスを返せる.
    • 評価が簡単
    • Singletonが使わなければインスタンスは決して⽣成されない.
    • オブジェクトの破壊が定義されていない
    • シングルトンへの参照を持ったあるクラスがそのシングルトンを破
    壊すると,別のクラスがそのシングルトンに参照しようとすると別
    のシングルトンインスタンスが⽣成されてしまう.
    • シングルトンを破壊するいい⽅法がない.
    • 継承できない
    • 単純に継承しただけでは派⽣クラスはシングルトンになってくれな
    い.
    • 効率性
    • インスタンスを⽣成するたび(以降,これをinstance()と呼ぶ)にIf
    ⽂が実⾏されるが,それが役⽴つのは最初の⼀回だけ.
    • 分かりづらい
    • Instance()を介することを知っていなければならない.
    6
    メリット デメリット

    View Slide

  7. .POPTUBUFύλʔϯ
    • 特徴
    • 異なる2つ以上のインスタンスに対してそれらが同⼀であるように振る舞わせることによって,共有するインスタンスの唯⼀性を
    保証する⽅法.
    • Singletonが唯⼀性を保証する構造に着⽬している⼀⽅で,Monostateパターンは振る舞いを保証している.
    • Monostateパターンは振る舞いを担保しているので,Singletonのテストケースもパス出来る.
    7
    final class Monostate {
    private static var value: Int = 0
    func set(value: Int) {
    Monostate.value = value
    }
    func get() -> Int {
    return Monostate.value
    }
    }

    View Slide

  8. .POPTUBUFύλʔϯΛ༻͍ΔϝϦοτٴͼσϝϦοτ
    • わかりやすさ
    • クライアントはMonostateを普通のオブジェクトと同じようなイン
    タフェースから扱える.
    • 派⽣可能
    • MonostateのサブクラスはMonostateである
    • ポリモーフィズム
    • Monostateのメソッドはstaticではないので,派⽣クラスの中でオー
    バーライド出来る
    • ⽣成と破壊の定義が明確
    • Monostateの変数はstaticであるから
    • 変換不可能
    • あるMonosteateではないクラスを継承してMonosteateにすると
    いったことが出来ない.
    • 効率性
    • Monostateは本物のオブジェクトを⽣成する
    • 存在
    • Monostateの変数は全く使われなくともメモリスペースを消費する.
    • プラットフォームに依存
    • JVMのインスタンスあるいは複数のプラットフォームから
    Monostateを共有することが出来ない.
    8
    メリット デメリット

    View Slide

  9. .POPTUBUFύλʔϯΛ༻͍ͨྫ
    • コインを⼊れたらアンロックされ通過出来る⾃動改札機を例とする.
    • ロックされた状態のときに(コインを⼊れずに)通過しようとするとアラームが鳴る.
    • アンロックされた状態のときにコインを⼊れると払い戻しされる.
    • Monostateパターンを⽤いて実装してみる.
    9

    View Slide

  10. .POPTUBUFύλʔϯΛ༻͍ͨྫ
    • 特徴
    • 異なる2つ以上のインスタンスに対してそれらが同⼀であるように振る舞わせることによって,共有するインスタンスの唯⼀性を
    保証する⽅法.
    • Singletonが唯⼀性を保証する構造に着⽬している⼀⽅で,Monostateパターンは振る舞いを保証している.
    • Monostateパターンは振る舞いを担保しているので,Singletonのテストケースもパス出来る.
    10
    class Turnstile {
    static var isLocked = true
    static var isAlarming = false
    static var coins = 0
    static var refunds = 0
    static let locked = Locked()
    static let unlocked = Unlocked()
    static var state: Turnstile = locked
    func reset() {
    Turnstile.isLocked = true
    Turnstile.isAlarming = false
    Turnstile.coins = 0
    Turnstile.refunds = 0
    Turnstile.state = Turnstile.locked
    }
    func deposit() {
    Turnstile.coins += 1
    }
    func refund() {
    Turnstile.refunds += 1
    }
    func pass() {
    Turnstile.state.pass()
    }
    func coin() {
    Turnstile.state.coin()
    }
    }
    final class Locked: Turnstile {
    override func coin() {
    Locked.state = Locked.unlocked
    Locked.isLocked = false
    Locked.isAlarming = false
    deposit()
    }
    override func pass() {
    Locked.isAlarming = true
    }
    }
    final class Unlocked: Turnstile {
    override func coin() {
    refund()
    }
    override func pass() {
    Unlocked.isLocked = true
    Unlocked.state = Unlocked.unlocked
    }
    }

    View Slide

  11. .POPTUBUFύλʔϯΛ༻͍ͨྫ
    • 特徴
    • 異なる2つ以上のインスタンスに対してそれらが同⼀であるように振る舞わせることによって,共有するインスタンスの唯⼀性を
    保証する⽅法.
    • Singletonが唯⼀性を保証する構造に着⽬している⼀⽅で,Monostateパターンは振る舞いを保証している.
    • Monostateパターンは振る舞いを担保しているので,Singletonのテストケースもパス出来る.
    11
    class Turnstile {
    static var isLocked = true
    static var isAlarming = false
    static var coins = 0
    static var refunds = 0
    static let locked = Locked()
    static let unlocked = Unlocked()
    static var state: Turnstile = locked
    func reset() {
    Turnstile.isLocked = true
    Turnstile.isAlarming = false
    Turnstile.coins = 0
    Turnstile.refunds = 0
    Turnstile.state = Turnstile.locked
    }
    func deposit() {
    Turnstile.coins += 1
    }
    func refund() {
    Turnstile.refunds += 1
    }
    func pass() {
    Turnstile.state.pass()
    }
    func coin() {
    Turnstile.state.coin()
    }
    }
    final class Locked: Turnstile {
    override func coin() {
    Locked.state = Locked.unlocked
    Locked.isLocked = false
    Locked.isAlarming = false
    deposit()
    }
    override func pass() {
    Locked.isAlarming = true
    }
    }
    final class Unlocked: Turnstile {
    override func coin() {
    refund()
    }
    override func pass() {
    Unlocked.isLocked = true
    Unlocked.state = Unlocked.unlocked
    }
    }
    2つの状態を派⽣クラスに譲渡(ポリモーフィズム可能)

    View Slide

  12. .POPTUBUFύλʔϯΛ༻͍ͨྫ
    • 特徴
    • 異なる2つ以上のインスタンスに対してそれらが同⼀であるように振る舞わせることによって,共有するインスタンスの唯⼀性を
    保証する⽅法.
    • Singletonが唯⼀性を保証する構造に着⽬している⼀⽅で,Monostateパターンは振る舞いを保証している.
    • Monostateパターンは振る舞いを担保しているので,Singletonのテストケースもパス出来る.
    12
    class Turnstile {
    static var isLocked = true
    static var isAlarming = false
    static var coins = 0
    static var refunds = 0
    static let locked = Locked()
    static let unlocked = Unlocked()
    static var state: Turnstile = locked
    func reset() {
    Turnstile.isLocked = true
    Turnstile.isAlarming = false
    Turnstile.coins = 0
    Turnstile.refunds = 0
    Turnstile.state = Turnstile.locked
    }
    func deposit() {
    Turnstile.coins += 1
    }
    func refund() {
    Turnstile.refunds += 1
    }
    func pass() {
    Turnstile.state.pass()
    }
    func coin() {
    Turnstile.state.coin()
    }
    }
    final class Locked: Turnstile {
    override func coin() {
    Locked.state = Locked.unlocked
    Locked.isLocked = false
    Locked.isAlarming = false
    deposit()
    }
    override func pass() {
    Locked.isAlarming = true
    }
    }
    final class Unlocked: Turnstile {
    override func coin() {
    refund()
    }
    override func pass() {
    Unlocked.isLocked = true
    Unlocked.state = Unlocked.unlocked
    }
    }
    派⽣クラスもMonostate

    View Slide

  13. .POPTUBUFύλʔϯΛ༻͍ͨྫ
    13
    class Turnstile {
    static var isLocked = true
    static var isAlarming = false
    static var coins = 0
    static var refunds = 0
    static let locked = Locked()
    static let unlocked = Unlocked()
    static var state: Turnstile = locked
    func reset() {
    Turnstile.isLocked = true
    Turnstile.isAlarming = false
    Turnstile.coins = 0
    Turnstile.refunds = 0
    Turnstile.state = Turnstile.locked
    }
    func deposit() {
    Turnstile.coins += 1
    }
    func refund() {
    Turnstile.refunds += 1
    }
    func pass() {
    Turnstile.state.pass()
    }
    func coin() {
    Turnstile.state.coin()
    }
    }
    final class Locked: Turnstile {
    override func coin() {
    Locked.state = Locked.unlocked
    Locked.isLocked = false
    Locked.isAlarming = false
    deposit()
    }
    override func pass() {
    Locked.isAlarming = true
    }
    }
    final class Unlocked: Turnstile {
    override func coin() {
    refund()
    }
    override func pass() {
    Unlocked.isLocked = true
    Unlocked.state = Unlocked.unlocked
    }
    }
    Monostateを普通のクラスに戻すのは難しい !

    View Slide

  14. ·ͱΊ
    ある特定のオブジェクトをアプリケーション上において1つだけしか⽣成したくない場合がある.
    Singleton/Monostateパターンはそういった場合に使えるテクニック.
    SingletonパターンではPrivateなコンストラクタと,static変数もしくはstatic関数を使ってインスタンスの
    ⽣成の制御を⾏う.
    Singletonパターンは既存のクラスに簡単な修正を加えるだけで⽤いる事ができる利点を持つが,クライア
    ントはインスタンスの取得⽅法について知っていなければならない.
    Monostateパターンは,クライアントにとってわかりやすい形で共有するインスタンスを取得出来る.
    また,ポリモーフィズムを利⽤しても唯⼀性を保証することが出来る.
    ⼀⽅で, Monostateパターンに依存するとその依存を剥ぐことや,既存のクラスにMonostateパターン適
    応するのはSingletonパターンに⽐較して難しい.
    14

    View Slide

  15. /VMM0CKFDUύλʔϯ
    15

    View Slide

  16. ໰୊
    • Nullを返し得るプロパティやメソッドに対して,Nullのチェックを⾏った後に参照を⾏う事がよくある.
    • しかし,このチェックを忘れてしまうこともしばしば.
    • Nullではなく特定のエラーを投げるようにコードを変更することによって,この問題は解決するが
    コードが醜くなる可能性がある.
    16
    let e = Employee().get(named: "Bob")
    if (e != nil && e.isTiemToPay(Date())) {
    e.pay()
    }

    View Slide

  17. /VMM 0CKFDUύλʔϯ
    • Nullチェックの対象となっていたクラス(インスタンス)を抽象化し,実装をインスタンスがNullの場合
    とそうでない場合に分ける.
    • 従来ではNullを返却してた変数もしくは関数をNullを返さずに,Nullを表すインスタンスを返却するよう
    に変更する.
    • Nullを表すクラスは何もしないように実装をする.
    17
    <>
    AnyObject
    Object
    NullObject

    View Slide

  18. /VMM 0CKFDUύλʔϯΛ༻͍ͨྫ
    18
    protocol EmployeeType {
    func isTiemToPay(_ date: Date) -> Bool
    func pay()
    }
    final class Employee: EmployeeType {
    private final class NullEmployee: EmployeeType {
    func isTiemToPay(_ date: Date) -> Bool {
    return false
    }
    func pay() {
    //何もしない
    return
    }
    }
    static let Null: EmployeeType = NullEmployee()
    func isTiemToPay(_ date: Date) -> Bool {
    // 何かしらの制御
    let shouldPay = true
    return shouldPay
    }
    func pay() {
    // 何かしらの制御
    }
    }
    // 何かしらの制御
    let employee = DB.get(named: "Bob")
    if employee.isTimeToPay(Date()) {
    faile()
    }
    let employee = DB.get(named: "Bob")
    if employee == Employee.Null {
    faile()
    }

    View Slide

  19. /VMM 0CKFDUύλʔϯΛ༻͍ͨྫ
    19
    protocol EmployeeType {
    func isTiemToPay(_ date: Date) -> Bool
    func pay()
    }
    final class Employee: EmployeeType {
    private final class NullEmployee: EmployeeType {
    func isTiemToPay(_ date: Date) -> Bool {
    return false
    }
    func pay() {
    //何もしない
    return
    }
    }
    static let Null: EmployeeType = NullEmployee()
    func isTiemToPay(_ date: Date) -> Bool {
    // 何かしらの制御
    let shouldPay = true
    return shouldPay
    }
    func pay() {
    // 何かしらの制御
    }
    }
    // 何かしらの制御
    let employee = DB.get(named: "Bob")
    if employee.isTimeToPay(Date()) {
    faile()
    }
    let employee = DB.get(named: "Bob")
    if employee == Employee.Null {
    faile()
    }
    何もしないNullEmployeeを作る

    View Slide

  20. /VMM 0CKFDUύλʔϯΛ༻͍ͨྫ
    20
    protocol EmployeeType {
    func isTiemToPay(_ date: Date) -> Bool
    func pay()
    }
    final class Employee: EmployeeType {
    private final class NullEmployee: EmployeeType {
    func isTiemToPay(_ date: Date) -> Bool {
    return false
    }
    func pay() {
    //何もしない
    return
    }
    }
    static let Null: EmployeeType = NullEmployee()
    func isTiemToPay(_ date: Date) -> Bool {
    // 何かしらの制御
    let shouldPay = true
    return shouldPay
    }
    func pay() {
    // 何かしらの制御
    }
    }
    // 何かしらの制御
    let employee = DB.get(named: "Bob")
    if employee.isTimeToPay(Date()) {
    faile()
    }
    let employee = DB.get(named: "Bob")
    if employee == Employee.Null {
    faile()
    }
    NullEmployeeは必ずfalseを返すので,
    Nullチェックは不要

    View Slide

  21. /VMM 0CKFDUύλʔϯΛ༻͍ͨྫ
    21
    protocol EmployeeType {
    func isTiemToPay(_ date: Date) -> Bool
    func pay()
    }
    final class Employee: EmployeeType {
    private final class NullEmployee: EmployeeType {
    func isTiemToPay(_ date: Date) -> Bool {
    return false
    }
    func pay() {
    //何もしない
    return
    }
    }
    static let Null: EmployeeType = NullEmployee()
    func isTiemToPay(_ date: Date) -> Bool {
    // 何かしらの制御
    let shouldPay = true
    return shouldPay
    }
    func pay() {
    // 何かしらの制御
    }
    }
    // 何かしらの制御
    let employee = DB.get(named: "Bob")
    if employee.isTimeToPay(Date()) {
    faile()
    }
    let employee = DB.get(named: "Bob")
    if employee == Employee.Null {
    faile()
    }
    NullEmployeeをシングルトンにすることで
    従来のような⽐較も可能

    View Slide

  22. ·ͱΊ
    • Null Objectパターンを⽤いることによって,Nullチェックを⾏う必要が無くなる.
    • なぜなら,たとえ何かしらの処理が失敗しても「なにもしない」オブジェクトが返却されることが保証
    されているから...
    22

    View Slide

  23. Ҿ༻
    • ロバート・C・マーチンほか.アジャイルソフトウェア開発の奥義 第
    2版 オブジェクト指向開発の神髄と匠の技. SBクリエイティブ, 2008
    23

    View Slide