LoginSignup
91
45

More than 3 years have passed since last update.

思いやりのSwift命名規則

Last updated at Posted at 2019-12-08

はじめに

これは、Swift Advent Calendar 2019 9日目の記事です。

コードレビューの際、何気に指摘が多いのが命名についてです。
命名に関するPRのコメントだけでも、対応すればCIが走るし、開発にかかる総時間は伸びてしまいます。

開発人数が多い場合はもちろん、一人で開発する場合でも、あとから見たときに混乱しない命名にするということは大切です。
しかしながら、命名に関しては頻繁な技術的アップデートが必要だったりするものではないので、一度コツをつかんで適切な命名がシュッとできるようになれば、レビュワーにとってもレビュイーにとっても負担が減ることになります。

今回は、多くのコードレビューを受けてきて、Swiftの命名について気をつけたいと感じたポイントを紹介します。命名規則と題していますが、規則というほどかっちりしたものではないです。

こちらの公式リファレンスも参考にしています。
Swift.org API Design Guidelines

型の名前を変数名に含めない

Swiftは型セーフな言語なので、変数定義の際に必ず何かしらの型に決まり、その変数には明示しなくとも型の情報が持たれることになります。

そのため、たとえローカル変数であっても、型名を変数名として使用するのは避け、何が入っている変数なのかという情報を与えるほうがベターです。

// Bad
let array = ["dog", "cat", "fox"]
// Good
let mammals = ["dog", "cat", "fox"]

ただ、UI部品の命名に関してはその次第ではなく、Apple標準ライブラリに倣い型名をSuffixとしてつけるのが一般的です。そうすることで、命名の重複を避けるという目的もあります。

振る舞いに合ったメソッド名をつける

たとえば返り値が無いメソッドなのにgetHogeみたいな命名をしてしまうと、これを見た別の開発者は何かしらのオブジェクトが返ってくるgetterの挙動を期待していまうかもしれません。

Swift Standard Libraryでは、何かしらの返り値がある場合は動詞の過去分詞形や名詞形・既存のオブジェクト等に対する操作をする場合には動詞の命令形で命名されていることが多いので、それらも参考に命名することで読む人の期待する振る舞いと実際の振る舞いが一致しやすくなります。

特に先の例のように、get・setといったワードは、安易にメソッド名に含めてしまうと読む人が期待するインターフェイスにならないことがあるので注意が必要です。

公開する情報が最小限になるようにする

アーキテクチャにも依りますが、TableViewのIndexPathの持つ情報や、ViewControllerのviewWillAppearなどのdelegateメソッドをトリガーとしたアクションを他のレイヤーに伝えたいとなったときに、これらの名前を直接公開するような命名をするのは推奨されません。

View以外に公開する命名では、Viewだけが知る情報名は隠蔽し、期待されるアクションに言及するような名前をつけるべきです。

ViewController.swift
import RxSwift
import RxCocoa

class ViewController: UITableViewController {
    // MARK: Internal
    // Bad
    let indexPathTrigger = PublishRelay<IndexPath>()
    let viewWillAppearTrigger = PublishRelay<Void>()
    // Good
    let selectTrigger = PublishRelay<Int>()
    let refreshTrigger = PublishRelay<Void>()

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        refreshTrigger.accept(())
    }
}
Presenter.swift
import RxSwift
import RxCocoa

class Presenter {
    weak var viewController: ViewController
    init(_ viewController: ViewController) {
        self.viewController = viewController
        viewController.refreshTrigger.asSignal()
          .emit(onNext: { _ in
              // ・・・
          })
    }
}

文章として読めることを意識する

例えば、キーワードで記事を検索するためのメソッドに対して以下のような命名をしたとしましょう。
すると、アクションを起こそうとしている対象がずれてしまっているように受け取れます。

// Bad
func search(name: String) {} // nameを探すかのように受け取れる

ここで外部引数に適切な情報を入れ込むことで、上記よりもずっとSwiftらしい命名になります。
呼び出しの部分を見ても、振る舞いがわかりやすくなっていることが分かるかと思います。

// Good
func search(byName name: String) {}
search(byName: "hoge")

ここではこのように「何で探すか」という情報を外部引数に入れていますが、外部引数にどこまで情報を入れるかというところは、実際の引数の型などに依ります。

また、Bool値を返すプロパティは、isEmptyisHiddenなどに習い、アクセスしたときにインスタンスの状態が一文で表されるような命名が良いとされます。

文章として読める命名にすることで、コードリーディングの際に必要な情報が名前に含まれ、余計なコメントを書く必要がなくなります。

まとまりがあるものには同じ命名・Prefix・Suffixを用いる

これはSwiftに限った話ではないかもしれませんが、共通項を持つメソッド・プロパティ達には命名にも共通項をもたせておくことで、特にコメントが書かれていなくてもそのことが他の開発者に伝わりやすくなります。

わかりやすい例として、引数に渡す型が異なるが施すアクションや返る結果が同じという場合には、オーバーロードを活用してメソッド名や引数名は同じものを用いてしまうというのが良いでしょう。
これにより、読む側はその引数の型が何であるかを意識せずに挙動だけを見れるというメリットもあります。

おわりに

この記事では、自分がこの1年命名に関して指摘を頂いてきたポイントをまとめてみました。
つまるところ命名規則というのは、読む人・共同開発者への思いやりだと思います。(主観です)
Swiftコードのレビューをお願いする前に、レビュワーや読み手を思いやり、是非今一度上記ポイントを思い出して確認してもらえると嬉しいです。

最後まで読んでいただきありがとうございました!
もっとこうしたほうが良い!私はこうしてる!等ありましたら是非コメントお願いします。

91
45
11

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
91
45