https://www.youtube.com/watch?v=YshUCzFpBqk
今回からは API Design Guidelines
にある 命名規則
の次のセクション 流暢な表現
を目指していくガイドラインを眺めていきます。それとその前に、前回に見てきた 自由度のある型情報を補う
ところで自分があまり理解できていなかった感があったので、それについても最初に振り返ってみようと思ってます。どうぞよろしくお願いしますね。
——————————————————————————————— 熊谷さんのやさしい Swift 勉強会 #15
00:00 開始 00:21 前回の捕捉 01:33 標準ライブラリーでの前置詞の扱い方 05:28 命名規則 06:02 関数名とメソッド名 15:14 意味の中心にはない引数ラベルの扱い 16:40 その引数自体がオプション的なのかで判断 21:12 イニシャライザーは別ルール 22:26 ファクトリーメソッド 26:33 イニシャライザーとファクトリーメソッドの引数ラベル 30:52 複数の要素で構成されるラベル名の付け方について 36:02 ガイドラインに対する心持ち 37:36 副作用の有無に応じた関数名 39:49 名詞の関数 42:04 副作用を持つのがわかりにくい関数 48:56 副作用の有無で対担っている関数 51:44 質疑応答 54:34 クロージング ———————————————————————————————
Transcription & Summarize : 熊谷さんのやさしい Swift 勉強会 #15
引き続き、APIデザインガイドラインの命名規則について見ていこうと思います。前回、少し前のスライドに戻ったんですが、このスライドについて自分の理解が甘くてうまく説明できなかった部分がありました。それでいろいろと時間をかけて、他の方々と30分ほど情報交換してみました。
基本的にはスライドの下部に出ている pokePath
や Observer
などを丁寧に補足としてラベルやメソッド名で説明していくのが一般的かつ通常のやり方だという印象でした。しかし、そうするとこのスライドが何を言いたいのかがよくわからなくなってきました。まだ確実な答えは見えていませんが、標準ライブラリがどんな感じになっているのかをリストアップしてみました。
例えば、insert a
というメソッドでは「インデックスで文字列を挿入する」という意図がわかりますが、標準の方法にはなっていない場合もあります。write to stream
では to stream
と書いていないメソッドもありますし、distance
についても from index to index
と書かれていないメソッドがあったりと、こうした例は他にもありました。
Foundationに含まれる他のメソッドもあれば、このような説明がついてくることが多いです。標準ライブラリでは全知詞だけのパターンを意図的に出している感じもします。しかし、prefix
に関してはラベルで何も説明されていない場合があったり、MemoryLayout
ではオフセットの部分にラベルがなかったり、一方で bindMemory
というメソッドには to
のあとに続くタイプの説明が省略されている場合が多いです。
このように標準ライブラリの作り方には一貫したルールが見えない場合もあるため、なんとなく感覚で行っている部分もあるかもしれません。全体的に見て、ラベルが意味を明確に伝えやすくする工夫がされている印象です。
命名規則ではAPIが自然に読めるようにすることが重要です。関数名やメソッド名は単独でも理解しやすく、また全体の構造を通してもしっかり意味が伝わるようにする必要があります。コメントの例も交えて、受信者(レシーバー)とメソッド名の関係が自然に読めるようなAPIを設計するのが理想です。
例えば、x
の subview
に havingColor y
という表現で、「色 y
を持つサブビュー」という形にすることが目指すべき方向です。このような表現を通して、自然な文法のように読み取れるAPI設計を行うことが重要です。
不適切な例として insert(y, position: z)
よりも insert y at position z
と読める方が自然です。このように、命名規則に従うことでAPIがドキュメントなしでも理解しやすくなります。
最後に、Swiftの命名規則は他の言語、特にC言語やJavaScriptと比べても、ラベルを利用して可読性を高める設計になっていると感じます。これによりコード自体がコメント代わりになるため、ドキュメントやコメントがなくてもコードの意図が伝わるような設計が可能です。
とりあえず、今回はこれで終わりにしますが、引き続き命名規則についてさらに詳しく見ていきたいと思います。 英語がネイティブな人にとっては、どんどんといい感じに宣伝していけるところですが、英語が苦手な私のような人間にとっては、ここがまず高いハードルとなるところでもあります。ただ、Swiftのおかげで少しずつ英語が学べている気がします。
さて、Swiftの関数名やメソッド名、ベース名、ラベル名を選ぶ際の基本ルールについてお話しします。まず、これが大きなポイントです。ですが、今述べたように、全てをスラスラと表現する必要はないのです。
APIにおいては、おまけ的な事柄については英文法の流れをそれほど考えなくても良いというガイドラインが用意されています。このガイドラインでは、オプションやコンプリションハンドラーのようなものは基本関係がない場合、あるいはそれほど重要でない場合、そのまま書いても問題ないとされています。
例えば、画面に出ている例を見てみましょう。with
の後にディスクリプションを取るようなところまでは、インスタンス化するために詳細情報が必要なので大事なポイントですが、オプションやコンプリションハンドラーは基本的に関係ありません。オプションがなくても何とかなるという場合もあります。
このような場合、英文法をあまり考えなくてもよいのです。重要なのは、それ自体がオプショナルなのかどうかを判断することです。例えば、ストリング型のイニシャライザーに整数型のイニシャライザーをストリングとして渡し、数字に変えるといったものがあります。ですが、これが主体となるなら、それに合わせたラベルやメソッド名を工夫する必要があります。
ここで、自分で簡単なコードを作成してみます。オプショナルの引数の紹介も兼ねています。
func makeInstance(name: String, userInfo: [String: Any]? = nil) {
// 処理
}
例えば、makeInstance
では名前(name
)は大事なポイントですが、userInfo
はおまけ情報です。この場合、userInfo
の部分に英文法をこだわって入れる必要はありません。そのため、userInfo
をそのまま使っても問題ありません。
大事なのは、これが主体となるかおまけとなるかというポイントです。それ自体にデフォルトパラメータがあるかどうかも判断基準となります。デフォルトでnil
を与えられるようなものは副次的なおまけパラメータなので、リュウチョウな英語にこだわる必要はありません。
もしデフォルトパラメータがない場合や、しっかりした英語表現が求められる場面では、それに合った引数名を使い、デフォルトパラメータを付けられる引数よりも前に持ってくるようにすると良いでしょう。スライドには書いてありませんが、そのような雰囲気で作っていく感じです。
ここでコメントでもらった質問ですが、例えば整数型のRadixのような例を考えると、そういうものはそれほど英文法を組み込まなくても良いという話をしようと思いましたが、イニシャライザーの場合、別のルールもありますので、それは別のスライドで説明します。
とにかく、普通のメソッドだったら、主体にならない引数ラベルは英文を意識しなくても大丈夫です。ファクトリーメソッドはインスタンスを作る役目があります。例えば、makeWidget
のようなものです。
例えば、スイッチ条件に応じて異なるイテレーターを返すケースを考えます。
switch condition {
case .x:
return AnyIterator<Int> { /* イントーを返すロジック */ }
case .y:
return AnyIterator<String> { /* ストリングを返すロジック */ }
default:
return nil
}
このような感じで、主体となる部分を意識しつつ、おまけ部分はあまり気にせずにコードを書いてみてください。 こうやって x
の時には何らかの値を返すイテレーターを作って、それで別のケースだった場合には、例えば配列の中からある配列のイテレーターを返したりします。こういう抽象クラスを作るのは、状況に応じて抽象クラスを選んで作ることになります。オブジェクト指向の設計ではベースクラスを返す設定にして、サブクラスをいろんな状況に応じて作って返す、というふうにしています。
Swiftでは抽象クラスを自由に作ることが難しくなってきましたが、この例では、イテレーターを作るときには make
という名前を付けて、これはファクトリーメソッドであることを表現するという決まりがあります。確かに make
が付いているとファクトリーメソッドだとすぐにわかりますが、それほど伝える必要があるのか疑問に感じました。とりあえず、分かりやすいことは確かです。一応ガイドラインとしてファクトリーメソッドを作る際には make
を頭に付けるといいみたいです。
今話している中で、ファクトリーメソッドという言葉をちゃんと理解していないような気がしてきたので、後で調べてみますね。ただし、ガイドラインとしてはファクトリーメソッドには make
を付けることになっています。
次に進みましょう。イニシャライザーとファクトリーメソッドの決まりごとについてです。さっきファクトリーメソッドのベース名について話しましたが、今度は引数ラベルの話です。基本的なメソッドでは英語で流暢な表現を目指すという話をしてきましたが、イニシャライザーやファクトリーメソッドについては、英文というのを意識した作りにはしないというガイドラインがあります。
第一引数の名前についてですが、これは何も第一引数に限った話ではなく、全ての引数が何であるかを端的に示します。API全体を見て説明するのではなく、イニシャライザーとファクトリーメソッドについてはその引数の値が何であるかに焦点を当てます。例えば、Color
クラスの場合、赤は32、緑は64、青は128とします。もし一般的なメソッドと同じような表現を選ぶとすると、非常にまどろっこしくなってしまいます。ガイドラインが無ければ、そのように書くかもしれませんが、ガイドライン的には Red
, Green
, Blue
を選びましょうとなります。
これをすることで見た目もスッキリして、意味も汲み取りやすい気がします。確かに良いガイドラインだと思います。
次に、フォアグラウンドの例について少し見てみましょう。自前で作った例を用意しておいたので、それを見てみます。上の方が推奨される形式で、下の方が非推奨の形式です。イニシャライザーの場合、上のような書き方が推奨されます。もし単なるメソッドであれば、makeColor
とするかもしれませんが、Swift では make
が付かないことも多い気がします。
これを普通のメソッドとして考えたとき、ガイドラインに沿ってない場合には、Red
, Green
, Blue
と一つのセットとして捉えます。その際、having
の部分を全部外側に持ってきて、グループをカッコの中で明確化します。このようにガイドラインに沿った書き方が推奨されます。また、having
の後にわざわざ RGBValues
と書かなくても、Color(having red:green:blue:)
のように書けば、パラメータ全体で十分に表現できるため、付け加える必要はありません。
これで一般メソッドとして作るとした場合、Color(having red:green:blue:)
という名前になるでしょう。時々こういったガイドラインをおさらいしつつ進めていくことが重要です。例えば、makeWidget
や makeColor
のような表現を使う場合もありますが、それを避ける方が良い場合もあるので、ガイドラインに従っていきます。
以上のように、時々振り返りつつ、ガイドラインに基づいて自然な言い回しやメソッド名を使っていきましょう。 ギアーズって表現するか、ギアアカウントって表現するか、みたいなところも違っていて、なかなか面白いですね。これは純粋に、ギアーズと型の宣言によってくるのかな。トラックと例えばウィジェットを作って、それでバリューズでね、バーでギアーズイントスタートなんだっけな。イントローズ。
多分ね、こういう風な表現になってるから、メイクウィジェットも同じようにパラメータを取っているんだと思うんですよね。これはもし、カウントだったら多分普通に、さっきの方もギアーズの2行目ね。上のブロックの2行目、メイクウィジェット、ギアアカウント、インドルカウント、みたいな表現の方が自然な気がします。
コメントの方でファクトリーメソッドのお話、詳しく教えてもらえてる様子ね。後でちょっと見てみますね。はい、そうなんですよね。なんかね、こういうガイドラインなかなか、自分の前回の最後のお話みたいなのもそうですけど、なかなか理解が曖昧だとブレるんですよね。今日はなんかこっちの表現が優勢かと思えば、次の日にはこっちの表現が優勢とかね。なんかそういう風にブレがちで。
でもだんだん理解を進めていくと、全体をイメージできるようになって、多少はそういうブレとかがなくなってくるので。あと日本語とか使う時もそうですよね。この表現適切かな不適切かな、みたいな。そういったのってなんか気分とか成長度合いとかで、だんだん変わってくるじゃないですか。そういった感じの、要は生き物みたいなものかな。日本語と同じように。そういう風なつもりで、ブレたらブレたでいいんじゃないですかね、きっと。
その中でも可能な限り良いだろうと思うものさえ追求していければ、きっとどんどん洗練されていくんだろうなと思います。
引き続きメソッド名の命名規則なんですけど、副作用に応じて関数の名前を変えていく、という基本原則があります。副作用を持たない関数は名詞で付ける。そうだっけ、なんかもっといっぱいありましたよね、ルールね。名詞、ちょっと次行っちゃいますね。
副作用を持たないものは名詞。うん、まぁそうか。副作用を持つ関数は命令形の動詞。うん、そうですね。うんうん、はい。なんかこの辺りいっぱいルールがあった気がしたな。とりあえずこういう基本原則があって、「distance
」ってなってたら副作用を持たないアクセッサー。
なんか自分の中で混乱を感じるが、副作用を持つものは動詞。うん、うん、「append
」。あんまり名詞のメソッドを見てないのかも、もしかすると。アクセッサー、アクセッサー、まぁそうね、アクセッサー。うん、そうかそうか。はい、まぁ、とりあえずこういうのがありますが。
ちょっと見てみよう、名詞の関数。なんだ、喋ってて混乱してるだけかな。「string
」、「append
」、命令形、副作用あり。「count
」、「count
」あ、これか、これ副作用なし。あ、なしね。うんうん。「customMirror
」、うん、副作用ないね。「description
ないね。あ、なんか普通にやってたね。うん。
名詞、「distance
」。「distance
」が名詞かどうかってなんか自分の英語力だと怪しくなってくるんだけど。まぁ、「encodeTo
」、「encode
」、「to
」、「encode
」、「endingがOK」。副作用、「
encode」、エンコードって副作用持つか? とか「
distance`」は名詞、素晴らしいね。なんか特定できるの素晴らしいわ。はい。
「hashValue
」。いやぁ、英語が全然伸びなくてね。学校の勉強で基礎を学んでも伸びないんですね。 感覚が間違っているんだろうな、isEmpty
、あ、isEmpty
はまた別のルールがある。ローアウトケース、副作用なし、ローアウトケースで。まぁ、色々あります。とりあえず、こうやって基本的な標準ライブラリのメソッドと照らし合わせてみたりすると、理解が深まってくるというところがあって、このライトみたいなのは副作用があります。
ライトツー、そうだよね。で、ここでちょっと面白かったのは、自分の中で「あ、これも副作用を持つ関数だったんだな」と再認識したことです。この例を見て、わかっている人には当たり前のことなんですけど、プリントも副作用を持つ関数です。副作用を持つ関数としてプリントしてあげられていたところ、勝手にある変数があって、この変数、例えば今だったらx
。x
は副作用がないんじゃないかと考えていましたが、暗黙的にそう思っていたみたいです。プリントが副作用を持つ関数ですよと言われたときに衝撃的でした。外部コンソールに出力するのも副作用です。
だから、こういうメソッドを作るときには副作用を持つメソッドだと捉えて、その命令形の動詞を選ぶというのが出てきます。他にも配列に関してもあります。例えば、formIndex
, index(after:)
。ちょっと例を出しちゃったな、後で出てくるんですけど、名詞のメソッドが副作用を持たない。で、その名詞のメソッドの副作用を持つ版を定義するときにはform
というプレフィックスを付けるルールがあります。
例えば、after
、1個後、2個後にしちゃおうか。4個にしておくか。そして、formIndex
っていうのはインデックスが変更できなければならない。index
、あ、formIndex(after:)
。何か書いていることがおかしい。formIndex(after:)
, inout
。あ、after
だからinout
が合っていますね。
次がインデックスです。例えば、0番目から1、2なのかな。何かうまく実行されていないです。配列をちゃんと取っておきますか。let array = [1, 2, 3, 4]
として、array.formIndex(after: &index)
。これが副作用を持たない版と持つ版。あ、ダブっちゃった。こうですね。
上がエラーを起こしているのか。副作用も面白くて、インデックスの次のインデックスを取る。だから0を渡したら1が取れているはずです。formIndex
の場合は、引数で渡したインデックスの次のインデックスを取得してインデックス変数に書き戻すメソッドになっています。これも副作用があるものです。
配列に対しては働きかけていないんですけど、受け取った引数のインデックスに対して働きかけています。こういったのも副作用を持つメソッドだと言われて、個人的には衝撃ポイントでした。今まで書いていた自分のコードを見つけると、この今の21行目のようなコードが普通に副作用なしのスタイルのメソッド名になっているものがいくつかありそうな気がします。この辺り、わかる人にとっては自然にわかることだとは思うんですけど、なるほど副作用ってそういうものなのかと思わせてもらったポイントでした。 間違った場合、コードが分かりにくくなることがあります。それは、皆さんにとっても分かりにくいコードになるということなので、よく改めたほうが良いでしょう。
はい、これまでの時間、今日のですね、今までの時間の中で一般的なメソッドの名前の付け方について話しました。具体的には、ファクトリーメソッドとイニシャライザのラベル名の付け方、そして副作用を持つメソッド名などについてです。さらに、副作用の有無に関して決められているメソッドのルールもあります。これは次回に回す予定ですが、スライドだけ軽く紹介しておきましょう。
ここで表示しているガイドラインでは、副作用を持つメソッドと持たないメソッドが並行して存在する場合があります。そのため、副作用の有無が分かりやすい名前を付けることが推奨されています。例えば、配列をソートする場合、配列そのものの順番を更新するメソッドは sort
という動詞の命令形にします。一方で、配列を変更せずに並び替えた結果だけを返すメソッドは sorted
という名前にすることで、副作用の有無が分かりやすくなります。
確か、Swift 2 のときには sort
で副作用なしの値を返していた時代があったんです。そのため、古いコードにはソース互換性がないことがすぐに分かると思います。過去のコードを見ていると、副作用のない sort
が存在するケースがありますが、これは余談として特に気にする必要はないでしょう。
さて、時間も程よく進んできたので、スライドを進めるのは今日はここまでにしましょう。あと2分半くらいありますが、コメントで sort
についての質問がありましたので回答します。sort
と sorted
はガイドラインの入門として非常に良い例です。ここから他のメソッドも同様のルールに基づいていることを理解しやすくなります。
例えば、エンコーディングにおいては encoder
に内容を詰め込むため副作用ありです。また、distance
は名詞であるため、ガイドラインに沿うのが難しいと感じることもあるでしょう。ただし、インターネットで簡単に調べられるので、英語の名詞形もすぐに分かります。
Apple 内蔵の Dictionary アプリも活用すると便利です。
以上で、今日の勉強会を終わりにしましょう。皆さんお疲れ様でした。ありがとうございました。