- 原文作者:Pablo Villar
- 译文出自:掘金翻译计划
- 译者:Zheaoli
- 校对者:Kulbear, Tuccuay
昨日、私はこの Jayme を Swift 3 に移行し始めました。これは私が初めて Swift 2.2 から Swift 3 にプロジェクトを移行する経験です。正直なところ、このプロセスは非常に煩雑で、Swift 3 は古いバージョンに基づいて多くの大きな変更があったため、時間がかかる以外に他の近道はないという事実を認めざるを得ません。しかし、この経験は一つの利点ももたらしました:Swift 3 に対する理解がより深まり、私にとってはこれが最良のニュースかもしれません。😃
コードを移行する過程で、多くの選択をしなければなりませんでした。さらに厄介なのは、全体の移行プロセスはコードを修正するだけではなく、Swift 3 にもたらされる新しい変化に少しずつ適応するための忍耐が必要だということです。ある意味では、コードを修正することは全体の移行プロセスの始まりに過ぎません。
もしあなたがコードを Swift 3 に移行することを決定したなら、あなたの長い旅の第一歩としてこの 記事 を読むことをお勧めします。
すべてが順調に進めば、近いうちに私はブログを書いて、移行プロセスのすべての詳細、私が下した決定などを記録するつもりです。しかし今は、非常に非常に重要な問題に集中します:関数の署名を正しく書く方法。
はじめに#
まず、Swift 3 と Swift 2 の関数命名方式の違いを見てみましょう。
Swift 2 では、関数の最初のパラメータのラベルは呼び出し時に省略可能で、これは 良い古き Objective-C の慣習 に従ったものです。例えば、次のようにコードを書くことができます:
// Swift 2
func handleError(error: NSError) { }
let error = NSError()
handleError(error) // Objective-C のように見える
Swift 3 では、関数を呼び出す際に最初のパラメータのラベルを省略する方法もありますが、デフォルトではそうではありません:
// Swift 3
func handleError(error: NSError) { }
let error = NSError()
handleError(error) // コンパイルエラー!
// ⛔ 呼び出し時に 'error:' の引数ラベルが欠けています
このような状況に直面したとき、私たちの第一反応は次のようになるかもしれません:
// Swift 3
func handleError(error: NSError) { }
let error = NSError()
handleError(error: error)
// 'error' を三回書かなければならなかった!
// 目が痛くなってきた 🙈
もちろん、こうすることで、あなたのコードがどれほど厄介になるかすぐに気づくでしょう。
前述のように、Swift 3 では関数を呼び出す際に最初のパラメータのラベルを省略することができますが、覚えておいてください、コンパイラにそれを明確に伝える必要があります:
// Swift 3
func handleError(_ error: NSError) { }
// 🖐 アンダースコアに注意!
let error = NSError()
handleError(error) // Swift 2 と同じ
あなたは、Xcode に付属の移行ツールを使用して移行する際に、このような状況に遭遇するかもしれません。
注意してください、関数署名のアンダースコアの意味は、関数を呼び出す際に最初のパラメータに外部ラベルが必要ないことをコンパイラに伝えることです。これにより、Swift 2 の方法で関数を呼び出すことができます。
さらに、Swift 3 が関数の書き方を変更した理由は、一貫性と可読性を保証するためです:異なるパラメータを区別する必要がなくなりました。これがあなたが直面する最初の問題かもしれません。
さて、コードはコンパイルして実行できるようになりましたが、あなたは何度も Swift 3 API design guidelines を読む必要があることを知っておくべきです。
☝️ 小さな人生の経験:常に Swift 3 API design guidelines を声に出して読む必要があります。これにより、Swift 開発の新しい体験を解放することができます。
第二歩、コードを簡潔にする#
では、前のコードをもう一度見てみましょう:
コードを簡潔にするために、あなたはコードを 修剪 することができます。例えば、関数名から型情報を削除するなどです。
// Swift 3
func handle(_ error: NSError) { /* ... */ }
let error = NSError()
handle(error) // 型名が削除されました
// 関数名から冗長だったため
もしあなたがコードをより短く、より洗練された、より明確にしたいのであれば、私はあなたに言います、指定された開発者として、この Swift 3 API design guidelines を繰り返し読むべきです。
関数の呼び出しプロセスが明確であることを確認し、以下の二点に基づいて関数の命名とパラメータを決定します:
- 私たちは関数の返す型を知っています
- 私たちはパラメータに対応する型を知っています(例えば、上記の例では、私たちはそのパラメータの型が NSError であることを疑う余地がありません)。
その他の問題#
今、私たちが議論していることを注意深く見てください。 ⚠️
上記の内容はすべての可能性を含んでいるわけではありません。言い換えれば、あなたは特定の状況に直面するかもしれません。つまり、あるパラメータの型がその役割を直感的に示すことができない場合です。
次のような状況を考えてみましょう:
// Swift 2
func requestForPath(path: String) -> URLRequest { }
let request = requestForPath("local:80/users")
もしあなたがコードを Swift 3 に移行したいのであれば、既存の知識に基づいて、次のようにするかもしれません:
// Swift 3
func request(_ path: String) -> URLRequest { }
let request = request("local:80/users")
正直、このコードは可読性が非常に低いです。少し修正してみましょう:
// Swift 3
func request(for path: String) -> URLRequest { }
let request = request(for: "local:80/users")
OK、今は見た目が良くなりましたが、上記の問題は解決していません。
この関数を呼び出すとき、私たちはどのようにしてこのパラメータに Web URL を渡す必要があるかを直感的に知ることができるのでしょうか?あなたが事前に知っているのは、String 型の変数を渡す必要があるということですが、Web URL を渡す必要があることは明確ではありません。
同様に、大規模なプロジェクトでは、各パラメータの役割を明確に理解する必要がありますが、明らかに、私たちはまだこの大きな問題を解決していません。例えば:
- どのようにして
String
型の変数が Web URL を表すことを知ることができますか。 - どのようにして
Int
型の変数が Http ステータスコードを表すことを知ることができますか。[String: String]
- どのようにして
[String: String]
型の変数が Http ヘッダーを表すことを知ることができますか。 - などなど…。
⚠️ 以上のことから、小さな人生の経験をお伝えします:コードを慎重に簡潔にしてください ✄
コードに戻ると、私たちはパラメータに対応するラベルを追加することでこの問題を解決できます。さて、次のコードを見てみましょう:
func request(forPath path: String) -> URLRequest { }
let request = request(forPath: "local:80/users")
さて、今コードはより明確で、可読性が向上しましたね? 🎉 おめでとうございます~
正直なところ、ここまで来たらブラウザを閉じてもいいですが、実際には、ここからが最も重要な部分です。
さて、関数のパラメータ命名に関する用語の問題を見てみましょう:
func request(forPath path: String) -> URLRequest { }
// 'path' という単語が二回出てきます
このコードは良さそうですが、もしあなたがそれをさらに良くしたいのであれば、次の部分を見てください。
あなたが知らない小技#
この小技は非常にシンプルです:文脈の中でパラメータの型と役割を反映させることで、あなたは無心でコードを簡潔にすることができます。
さて、次のコードを見てみましょう。
typealias Path = String // 救世主!
func request(for path: Path) -> URLRequest { }
let request = request(for: "local:80/users")
この例では、パラメータの型と役割が完璧に統一されました。なぜなら、文脈の中で String
に Path
という別名を付けたからです。
今、あなたの関数は依然として簡潔で、可読性が高いですが、重複はありません。
同様に、あなたは同じ方法を使って美しいコードを書くことができます:
typealias Path = String
typealias StatusCode = Int
typealias HTTPHeader = [String: String]
// など...
ご覧の通り、あなたは簡潔で美しいコードを書くことができます。
ただし、覚えておいてください、すべてのことが極端になると味が変わります:この小技はあなたのコードに追加の負担をかける可能性があります。特に、コードに多重ネストがある場合は注意が必要です。したがって、無心でこのような小技を使用すると、痛い代償を払うことになるかもしれません。
結論#
多くの場合、Swift 3 を使用する際に、関数を命名する際に多くの困難に直面します。
いくつかのコードスニペットを蓄積することがあなたを大いに助けるかもしれません:
func remove(at position: Index) -> Element { }
employees.remove(at: x)
func remove(_ member: Element) -> Element? { }
allViews.remove(cancelButton)
func url(forPath path: String) -> URL { }
let url = url(forPath: "local:80/users")
typealias Path = String // 代替
func url(for path: Path) -> URL { }
let url = url(for: "local:80/users")
func entity(from dictionary: [String: Any]) -> Entity { /* ... */ }
let entity = entity(from: ["id": "1", "name": "John"])