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

それをEnumるなんて とんでもない! - Swift Enum antipattern

Avatar for takasek takasek
November 29, 2016

それをEnumるなんて とんでもない! - Swift Enum antipattern

AKIBA.swift 第7回 - connpass
https://classmethod.connpass.com/event/44812/
での発表資料です。

Avatar for takasek

takasek

November 29, 2016
Tweet

More Decks by takasek

Other Decks in Programming

Transcript

  1. final class ΅͏͚Μͷ͠ΐListViewModel { enum LoadingState { case idle case

    proceeding case succeeded case error } let loadingState = Variable<LoadingState>(.idle) let saveData = Variable<SaveData?>(nil) func fetchSaveData(at index: Int) { SaveDataAPI(of: index).fetch { [weak self] (data: SaveData?) in if let data = data { self?.saveData.value = data self?.loadingState.value = .succeeded } else { self?.loadingState.value = .error } } } 10
  2. final class ΅͏͚Μͷ͠ΐListViewModel { enum LoadingState { case idle case

    proceeding // !͍ͭ͜Β͸͔֬ʹʮ௨৴ঢ়ଶʯ͚ͩͲ… case succeeded case error // !͍ͭ͜Β͸ʮ௨৴݁ՌʯΛදͯ͠Δ } let loadingState = Variable<LoadingState>(.idle) let saveData = Variable<SaveData?>(nil) //ɹ ! SaveDataͱLoadingState.error ͸ //ɹɹɹͲͬͪ΋ʮ௨৴݁Ռʯͱݴ͏ҙຯͰ໾ׂ͕ॏෳ 12
  3. ͬͪ͜ͷઃܭͷ΄͏͕ਖ਼͍͠ final class ΅͏͚Μͷ͠ΐListViewModel { enum LoadingState { case idle

    case proceeding //!௨৴ঢ়ଶ͚ͩΛࣔ͢Enumʹ͠·͠ΐ͏ //"Ή͠Ζ Bool ͰࡁΉ } let loadingState = Variable<LoadingState>(.idle) let saveData = Variable<Result<Error, SaveData>?>(nil) //!௨৴ޙ͸ɺ݁Ռ(Error or SaveData)͕ඞͣೖΔ 13
  4. 14

  5. struct Character { enum Job: String { case Ώ͏͠Ό =

    "hero" case ͤΜ͠ = "soldier" case ·΄͏͔͍ͭ = "wizard" case ͦ͏Γΐ = "priest" case undefined } let job: Job ... init(json: JSON) { ... job = Job(json["job"].stringValue) ?? .undefined ... } 16
  6. enum Job: String { case Ώ͏͠Ό = "hero" case ͤΜ͠

    = "soldier" case Ϳͱ͏͔ = "fighter" case ·΄͏͔͍ͭ = "wizard" case ͦ͏Γΐ = "priest" case undefined // ! ཻ౓͕͓͔͍͠ } let job: Job // ! Required ͜͏͡Όͳͯ͘ɺ 18
  7. enum Job: String { case Ώ͏͠Ό = "hero" case ͤΜ͠

    = "soldier" case Ϳͱ͏͔ = "fighter" case ·΄͏͔͍ͭ = "wizard" case ͦ͏Γΐ = "priest" //case undefined ͳͲͳ͍ } let job: Job? // ! Optional ͜͏Ͱ͠ΐʂ 19
  8. ▶ ผΧςΰϦͷcase͕ࠞͬͨ͟Enumɹᶄ ΋ͬͱݴ͑͹ɺ let job: Job // ! Optionalʹͨ͘͠ͳ͍ͳΒ init?(json:

    JSON) { guard job = Job(json["job"].stringValue) else { // Enum͕࡞Εͳ͍৔߹ɺ // failable initializerʹͯ͠ܕ͝ͱ௵ͦ͏ return nil } self.job = job ... } 20
  9. 26

  10. final class μʔϚͷਆ఼ViewModel { /// TableViewCellͷrowͱׂͯ͠Γ౰ͯΔ഑ྻ let options: [Character.Job] =

    [ //.Ώ͏͠Ό, !స৬ෆՄೳͳͷͰলུ .ͤΜ͠, .Ϳͱ͏͔, .·΄͏͔͍ͭ, .ͦ͏Γΐ ] let targetCharacter: Character func select(_ selection: Character.Job) { switch selection { case .Ώ͏͠Ό: return //Ώ͏͠Όʹ͸స৬Ͱ͖ͳ͍ default: changeJob( of: targetCharacter, to: selection) } } ... } 29
  11. switch selection { case .Ώ͏͠Ό: return //Ώ͏͠Όʹ͸స৬Ͱ͖ͳ͍ default: changeJob(of: targetCharacter,

    to: selection) } ! ࠓճ͸ૣظreturnͰࡁΉύλʔϯ͔΋͠Εͳ͍͚Ͳɺ ৔߹ʹΑͬͯ͸ɺ࠷ѱॻ͚Δॲཧ͕ͳͯ͘ fatalError() ͠ͳ͖Ό͍͚ͳ͍΍ͭͩ 31
  12. ίϯςΩετ͕ҧ͏ͳΒɺ ͦΕ͸ผͷEnumͰ؅ཧ͢Ε͹͍͍ ͭ·Γɺ͜͏Ͱ͸ͳ͘… final class μʔϚͷਆ఼ViewModel { let options: [Character.Job]

    func select(_ selection: Character.Job) { switch selection { case .Ώ͏͠Ό: return //Ώ͏͠Όʹ͸స৬Ͱ͖ͳ͍ default: changeJob(of: targetCharacter, to: selection) } } ... } 37
  13. ίϯςΩετ͕ҧ͏ͳΒɺ ͦΕ͸ผͷEnumͰ؅ཧ͢Ε͹͍͍ ͜͏ʂ final class μʔϚͷਆ఼ViewModel { enum ChangableJob {

    //స৬ͱ͍͏ίϯςΩετͰͷΈ࢖͏ʂ case ͤΜ͠, Ϳͱ͏͔, ·΄͏͔͍ͭ, ͦ͏Γΐ } let options: [ChangableJob] func select(_ selection: ChangableJob) { //બ୒ࢶʹ͋Δ͠ΐ͗͘ΐ͏͔͠౉ͬͯ͜ͳ͍ //΋͏ɺΏ͏͠Όͷ͜ͱ͸ߟ͑ͳͯ͘ྑ͍…ʂ changeJob(of: targetCharacter, to: selection) } ... } 38
  14. 40

  15. struct Equipment: Item { enum Kind { case Ϳ͖ case

    ΑΖ͍ case ͨͯ case ͔Ϳͱ } let kind: Kind ... } 43
  16. final class ͦ͏ͼListViewModel { let character: Character //ର৅Ωϟϥ let equipments:

    [Equipment] //TableViewʹදࣔ͢ΔΞΠςϜͷϦετ init(character: Character, for kind: Equipment.Kind) { self.equipments = character.items //Ωϟϥͷ͍࣋ͬͯΔΞΠςϜͷதͰɺ .flatMap { $0 as? Equipment } // ͦ͏ͼͰ͋Γɺ .filter { $0.kind == kind } // ࢦఆ͞ΕͨछผͱҰக͢Δ΋ͷΛTableViewʹදࣔ self.character = character } func select(equipment: Equipment) { //ͦ͏ͼͷछྨʹΑͬͯɺ΍Δ͜ͱ͕มΘΔ switch equipment.kind { case .Ϳ͖: performProcess(asWeapon: equipment) case .ΑΖ͍: performProcess(asArmor: equipment) case .ͨͯ: performProcess(asShield: equipment) case .͔Ϳͱ: performProcess(asHelmet: equipment) } } private func performProcess(asWeapon weapon: Equipment) { guard case .Ϳ͖ = weapon.kind else { return } ... } ... } 44
  17. ͜ͷίʔυɺ ݏͳ೏͍͕͠·͢Ͷ switch equipment.kind { case .Ϳ͖: performProcess(asWeapon: equipment) case

    .ΑΖ͍: ... } private func performProcess( asWeapon weapon: Equipment) { guard case .Ϳ͖ = weapon.kind else { return } ... 46
  18. ͳΒɺ͜ΕͰ͍͍Μ͡Όͳ͍ʁ struct Weapon: Item, Equipable { ... } struct Armor:

    Item, Equipable { ... } struct Shield: Item, Equipable { ... } struct Helmet: Item, Equipable { ... } 52
  19. final class ͦ͏ͼListViewModel<E: Item, Equipable> { let character: Character //ର৅Ωϟϥ

    let equipments: [E] //TableViewʹදࣔ͢ΔΞΠςϜͷϦετ init(character: Character) { self.equipments = character.items //Ωϟϥͷ͍࣋ͬͯΔΞΠςϜͷதͰɺ .flatMap { $0 as? E } // ͦͷͦ͏ͼछผͷ΋ͷΛTableViewʹදࣔ self.character = character } func select(equipment: E) { switch equipment { case let e as Weapon: performProcess(weapon: e) case let e as Armor: performProcess(armor: e) case let e as Shield: performProcess(shield: e) case let e as Helmet: performProcess(helmet: e) default: () } } // ܕϨϕϧͰ Ϳ͖ ͩͱಛఆͰ͖͍ͯΔʂʂ private func performProcess(weapon: Weapon) { ... } } 53
  20. 57

  21. ࠷ޙʹɺ EnumΛ࢖͏΂͖͔νΣοΫγʔτ 4 ͦͷάϧʔϓ͸switchจͰ໢ཏ͢Δඞཁ͕͋Δ͔ʁ 4 ͳ͍ͳΒɺprotocol + ݸผͷstructͰྑ͍ͷͰ ͸ʁ 4

    ͋Δ͍͸άϧʔϐϯάࣗମɺෆཁͳͷͰ͸ʁ 4 ΧςΰϦͷҧ͏case͕ࠞͬͯ͟ͳ͍͔ʁ 4 ͦͷEnum͸ɺίϯςΩετʹԊͬͨ΋ͷ͔ʁ 58
  22. 59

  23. !…ͱ͸͍͑ String enum͸ศརͰ͚͢ͲͶͬʂ enum ;͔ͬͭͷ͡Ύ΋Μ: String { case lv25 =

    "΄Γ͍Ώ͏ɹ͑͡ʹͭ͘͢ͲɹΒ͑͘͢͝ɹͱͩΑ" case lv20 = "·Δ͔ͭ͸ɹ΍ͭ͸Γ͔͍ͤɹ͍ͪͩͭͨɹͷͩΑ" case lv24 = "ΓΏ͏͓͏ɹ͓·͑͸΋͏͠ɹ͵Θ͔ͭͨɹ͔͹͔" case lv27 = "ͲΒ͑͘͸ɹͶͱ͛ʹͳͭͯɹͭ·Βͳ͍ɹ͋͏ͱ" case lv17 = "͑;͑;͸ɹͲΒ͑͘ΑΓ΋ɹ͓΋͠Ζ͍ɹ·͡͞" } passwordTextView.text = ;͔ͬͭͷ͡Ύ΋Μ.lv25.rawValue 60