くま's Tech系Blog

基本的には技術で学んだことを書き留めようと思います。雑談もやるかもね!

Swift API Design Guidelinesの命名について

Swift API Design Guidelinesという実装でのコーディングルールをまとめた公式ドキュメントについてです

コメントの書き方や命名などのコーディング規約のようなものです。今回は命名についてまとめようと思います

今回は自分の英語の勉強のアウトプットが主な目的になるので、下記のドキュメントをご自身で確認するのをオススメします!

www.swift.org

命名

曖昧さを避けるために全ての単語を含めよう

呼び出す人にとって曖昧な意味と捉えられないように、全ての単語を含めましょう

下記パターンは良い例です

extension List {
  public mutating func remove(at position: Index) -> Element
}

employees.remove(at: x)

しかし次のパターンは良くない例です

employees.remove(x)

よくないパターンだとx番目のデータを削除するのかxというデータ自体を削除するのか呼び出す人は判断しにくいです。 今回はatをつけることでどんな処理を行うのかがわかりやすくなるため単語で補完できるようにするのが望ましいです

不必要な単語は省く

先程、曖昧さを避けるために全ての単語を含めようと述べたのに?と思う人もいるかもしれません

全ての単語は、呼び出し側にとって意味のある情報であるべきであって、意図を明確にしたり意味の違いを明確にするために多くの単語を使うこともあります

ただ、読み手にとって不要な情報(なくても理解できるもの)は省略しよう

例えば次のような例は望ましくないです

public mutating func removeElement(_ member: Element) -> Element?

戻り値の型がElementなのは定義を確認したらわかるようになっているので、関数名がremoveElementとなっているのは冗長です

次の方が望ましいです

public mutating func remove(_ member: Element) -> Element?

役割に応じて変数・パラメータ・関連型を命名する

型制約ではなく、役割に応じて変数・パラメータ・関連型を命名するのが望ましいです

var string = "Hello"
protocol ViewController {
  associatedtype ViewType : View
}
class ProductionLine {
  func restock(from widgetFactory: WidgetFactory)
}

上記の場合だとvar stringassociatedtype ViewTypewidgetFactoryなど型名を入れていて何の役割を果たすのかがわかりにくいです。 下記のように役割を明確にするような命名が望ましいです

var greeting = "Hello"
protocol ViewController {
  associatedtype ContentView : View
}
class ProductionLine {
  func restock(from supplier: WidgetFactory)
}

ただし、関連付いた型がProtocol制約に密接に結びついて、Protocol名が役割になっている場合、 衝突を回避するためにProtocol名にProtocolを付けるのは問題ないです

弱い型情報には捕捉を行うようにする

パラメータ型がNSObject, Any, AnyObject, Int, Stringなどの基本型の場合、使用時の型情報と名前で意図をちゃんと伝えられない可能性があります。 下記の場合には定義を見ると内容が理解できるかもしれないですが、使用する場合には何が行われるのかがわからない可能性が高いです

func add(_ observer: NSObject, for keyPath: String)

// 曖昧
grid.add(self, for: graphics)

そのため次のように基本型の前に役割を示す名詞を付けるなどの補足を行い呼び出す側でも処理を理解できるようにします

func addObserver(_ observer: NSObject, forKeyPath path: String)
grid.addObserver(self, forKeyPath: graphics)

スムーズに英語を読む感覚(文法的)で命名する

例えば、下記の処理はx insert y position zと読むことになります。 ただし、文法的には正しくないと思います

x.insert(y, position: z)

下記の場合であれば、x insert y at zとなり文法的にも正しいです。 役割のわかりやすさだけでなく文法的にも正しいのかをチェックするのが望ましいです

x.insert(y, at: z) 

ただし、第二引数より後の引数が呼び出しにとっての中心的な役割を持たない場合、文法的におかしくても許容されます

逆にいうと、呼び出しの意味の主要なものから引数にしていくのが望ましいのかもしれません

initializerやfactoryメソッドの最初の引数は、関数名で始まる英語フレーズにしなくていい

今まではスムーズに英語を読む感覚で命名すると記載しましたが、例外があります

initializer(初期化処理)やfactoryメソッドの場合には文法的でスムーズに英語を読む感覚で定義する必要はありません。 下記のように初期化処理で第一引数を含めたフレーズにする必要はありません

let foreground = Color(havingRGBValuesRed: 32, green: 64, andBlue: 128)
let newPart = factory.makeWidget(havingGearCount: 42, andSpindleCount: 14)
let ref = Link(to: destination)

次のように引数名はスムーズに読めるような形にする必要はありません

let foreground = Color(red: 32, green: 64, blue: 128)
let newPart = factory.makeWidget(gears: 42, spindles: 14)
let ref = Link(target: destination)

副作用によって関数とメソッドの名前をつける

ガイドラインでは「副作用に応じてメソッドに名前を付ける」と記載されています

副作用とは、メソッドの呼び出しによってオブジェクトの状態、または別のオブジェクトの状態が変化することを指します

副作用があれば名前を動詞に、副作用がなければ名前を名詞にすることが推奨されています

そして、可変(mutating)・不変(nonmutating)メソッドのペアは一貫した命名をしましょう

インスタンスの内容を更新するメソッド(可変) インスタンスの内容を更新しないメソッド(不変)
x.sort() z = x.sorted()
x.append(y) z = z.appending(y)

操作が動詞で自然に説明できる場合は、その命令形をインスタンスの内容を更新するメソッドに使います

そして、edまたはingをつけた名前を対応するインスタンスの内容を更新しないメソッドに使います(動詞が直接目的語を持つためedをつけるのが文法的に正しくない場合に動詞の現在分詞を使用しingをつけてインスタンスの内容を更新しないメソッドに名前をつける)

また、操作が名詞で自然に説明できる場合はインスタンスの内容を更新しないメソッドにその名詞を使い、対応するインスタンスの内容を変更するメソッドには頭にformをつけた名前をつけましょう

インスタンスの内容を更新するメソッド(可変) インスタンスの内容を更新しないメソッド(不変)
x = y.union(z) y.formUnion(z)
j = c.successor(i) c.formSuccessor(&i)

その他に以下のような規約があります

  • インスタンスの内容を更新しないBool 値を返すメソッドとプロパティはレシーバに関する表明として読めるべき 例: x.isEmpty, line1.intersects(line2)
  • 何であるのか を記述するプロトコルは名詞として読めるべき(例: Collection)
  • 能力 を記述するプロトコルは接尾語 able, ible, ing を使用して名前をつけるべき (例: Equatable, ProgressReporting)
  • その他の型、プロパティ、変数、定数の名前は名詞として読めるべきです

専門用語を使う際に気をつけるポイント

より一般的な言葉で意味が伝わる場合は、よくわからない専門用語を避けましょう。 「皮膚(skin)」でも良い場合「表皮(epidermis)」とは言わないでください。 専門用語は重要な意味が失われてしまわないようにする時だけ使用してください

一般用語より専門用語を使うのは、それがなければ曖昧で不明瞭になるモノを正確に示すために使用します。 例えば、API のような技術用語も確立された意味を示す場合に使いましょう

逆に専門用語に慣れ親しんだ人にとって、違う意味で使おうとしないでください

また、略語は避けましょう。 略語、特に一般的ではないものは、実質的に技術的専門用語です。 なぜなら理解できるかどうかは略されていない形式に正しく翻訳できるかどうかに拠るためです

そして、既にある文化への適合性を無駄にしてまで、初心者向けに用語を最適化しないでください

配列を使う場合に、Listの方が意味を取りやすいかもしれませんが、連続したデータ構造はListのような単純な用語よりもArrayと名付ける方がよいです。 配列は現代のコンピューティングの基礎であるため、全てのプログラマーは配列とは何か知っています。 このように全てのプログラマーが慣れ親しんでいる用語を優先的に使いましょう。 また、数学のような特定のプログラミングの分野ではverticalPositionOnUnitCircleAtOriginOfEndOfRadiusWithAngle(x)といった説明的なフレーズよりsin(x)といった広く認められている用語が好ましいです。 完全な単語はsineですが、sin(x)は何十年ものあいだプログラマーにとって一般的なので、sinを使うべきです