Posted at

【問題提議】Enumは、解釈できないrawValueに対応するCaseを作るべきかOptionalを使うべきか

More than 3 years have passed since last update.


バックストーリー


問題

ある日、あなたのチームに、iOSアプリがクラッシュする!というバグ報告が押し寄せました。

どうやら新規実装された「武内P」というキャラクターが問題のようです。

ソースを確認してみます。


response.json

{

id: 346,
name: "武内P",
type: 3
}

class Idol {

enum Type: Int {
case Cute
case Cool
case Passion
}
let id: Int
let name: String
let type: Type

init(json: JSON) {
id = json["id"].stringValue
name = json["name"].stringValue
type = Type(rawValue: json["type"].intValue)!
}
}

原因がわかりました。 Idol.Type のrawValueは0〜2の3パターンを想定しているのに、実際のresponseには 3 が入ってきています。

サーバチームに確認したところ、この「武内P」は、新しいtypeを持つキャラクターなのだそうです。

Typeは3パターンで決めうちだと聞いてたのに増えるなんて! と愚痴りつつも、あなたは取り急ぎPull Requestを出しました。

    enum Type: Int {

case Cute
case Cool
case Passion
+ case Producer
}

Enumのcaseをひとつ増やして、これで一安心…と思ったら、チームメンバーからレビューがつきました。


ふたつの解答

Oさんのレビュー。


O:

これだとダメ。また新しいTypeが実装された途端、再びクラッシュするから。

type = Type(rawValue: json["type"].intValue)!

のforced unwrapをなくして、このクラスの type プロパティはOptionalに変えよう。

解釈できない状態はnilで表現すべき。


そこに、チームメンバーのFさんから横レスがつきました。


F:

えー?

外からtypeを参照するときいちいち if let でunwrapするの面倒じゃない?

それよりさー、新しいcase Type.None を追加して、

type = Type(rawValue: json["type"].intValue) ?? .None

って書けば一番楽だと思うな



ふたつの立場の立脚点

ここからは二人の論戦が続きます。

O「 Type.None ? 解釈できないだけで存在してるのに?」

F「ネーミングの問題ー? じゃあ Type.Unrecognized にしよう」

O「命名の問題じゃない。

 Type.Unrecognized のrawValueは? 5だよね。

 レスポンスの値がたとえば7でもそのcaseになると…値が乖離しちゃう」

F「それ、気にするとこー?

 …まーぶっちゃけstringで返してほしかった的なとこはあるよねー…って話はおいといて。

 rawValueが問題なら、enum Type: IntInt を削ってから init(id:Int) みたくイニシャライザを用意すればOK?」

O「そういうことじゃなくて。

 実際は Unrecognized なんてTypeはないんでしょ。

 存在する状態と存在しない状態が別れる場合、Swiftに言語仕様としてOptionalが用意されてるのだから、Optionalを使うべきってこと」

F「うーん…

 実運用上、解釈できないTypeなんて異常値でしょー?

 そのためにプロパティをOptionalにしたら、アプリ全体でunwrapの手間が増えちゃうじゃん。めんどくさくなーい?

 Typeが解釈できないってのも、列挙できるひとつの状態だよ。そもそもOptional型だって所詮 SomeNone のEnumなんだし。

 言葉を返すけど、列挙できる場合、Swiftに言語仕様として列挙子が用意されてるのだから、列挙子を使うべきじゃないかなー」

OさんとFさんは、視点が違うだけで、どちらももっともらしいことを言っているように見えます。


さて、あなたはどう考えますか?

このエントリに結論はありません。

コメント等、お待ちしています。