- 原文作者: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"])