LoginSignup
2
1

More than 1 year has passed since last update.

ひみつのポエム(見ちゃいやん)

Last updated at Posted at 2022-02-25

その場の思いつきで書きなぐった自分用のポエム物置き場です。あまり考えないで書きなぐったうえで後に精査する気もほとんどないヤツです。今これを見ているあなたはエッチです。えっちなのはいけないと思います。

■ Conding Convensions 試行錯誤

  • 関数の先頭から戻り値の型まで、
    • 引数が1つの場合は1行。
    • 引数が2つかつ、デフォルト値の設定が無いかつ、1行に収めるのに長すぎるということが無い場合は1行。
    • 引数が3つ以上かつ、同種の引数のグループ毎に1行かつ、各グループの行が1行に収めるのに長すぎるということが無い場合は、引数をグループ化。
    • 上記以外の場合は、引数のグループ化をせずに改行。

■ Class layout に関して

はじめに

Kotlin の coding conventions に関して

Kotlin の coding conventions の Class layout には以下のように書かれている。

The contents of a class should go in the following order:

  1. Property declarations and initializer blocks

  2. Secondary constructors

  3. Method declarations

  4. Companion object

Do not sort the method declarations alphabetically or by visibility, and do not separate regular methods from extension methods. Instead, put related stuff together, so that someone reading the class from top to bottom can follow the logic of what's happening. Choose an order (either higher-level stuff first, or vice versa) and stick to it.

Put nested classes next to the code that uses those classes. If the classes are intended to be used externally and aren't referenced inside the class, put them in the end, after the companion object.

しかし、おいらの普段の作業の流れをベースに考えると結構悩ましい。

◆Do not sort the method declarations by visibility.

まず、visibility でソートするなと書かれているのだが、おいらがクラスのコードを読む際には、visibility を意識して読むことが多い1。呼び元の文脈からコードを読む場合は呼び元が interface に対して書かれているのであれば interface を見るし、そうでなければ public scope の情報を見る。継承がらみの保守であれば protected scope も見る。という感じで、まずは全体を確認する。以降はクラス全体の設計を見直すような場合はそこから狭いスコープを順次確認もしくは上位スコープのメソッド2を辿る感じで振る舞いを確認していくし、バグ対応などでバグ発生箇所が分かっているような場合は発生箇所から呼び出しの流れを逆に辿っていく感じで進めることが多い。

どちらのケースにしても、visibility でソートされてないと結構キツイ。

◆Do not sort the method declarations alphabetically.

alphabetically にソートするなという件に関しては、するべきであると言う気はないが、絶対にするなというのも違う気がする。個人的には private 以外のスコープに関しては、可読性の高い順序というのがある気がしている。private 以下も同様に思っているが、正直 alphabetical でもあんまり困る気はしないかも。

◆Do not separate regular methods from extension methods.

これは同意。
いや、具体例出して考えてみたほうがいい気がしてきた、、、。ちゃんと考えよう。

◆Put related stuff together, so that someone reading the class from top to bottom can follow the logic of what's happening. Choose an order (either higher-level stuff first, or vice versa) and stick to it.

まず、雑音が多くなりすぎる危険があってとても無理。例えば、非常に巨大な単一の public method があるとして、それを大量に extract method で private method 出しをしたとして、それが public method の下に何画面分もあるとしたら、可読性が低すぎてやってられない。

また、Choose an order (either higher-level stuff first, or vice versa) and stick to it と言うは易し。例えば、クラス上に同一ロジックがあらゆる visibility のメソッド上に存在するとして、それを private method として extract した際に、どこに置けばいいの?

さらに、上記の例で自明だが、メソッド呼び出しは、単方向制約の付いた有効グラフになることが多く3、親ノードが複数存在し得、また、親ノードのグラフ上の位置が全体にわたるようなことも普通にある。このような親ノードを複数持つノードが大量にある状態で、順序を選んでさらに stick to it とか、無理ゲーじゃね?

この件については、Put nested classes next to the code that uses those classes も同様で、複数から使われてたらどうするの?って話。

◆If the classes are intended to be used externally and aren't referenced inside the class, put them in the end, after the companion object.

これも visibility で分けたい気がする。public, internal, protected のクラスを無秩序に並べると、外部からの視点だけで俯瞰したい時の雑音になる。

■ Visibility に対応した順序について

何気に backing field を持たない val/var とか、カプセル化の有無とか、primitive であるかどうかとか、関係しそうなことが多岐に渡っていてかなり悩ましいのかと思いつつ、実はそうでないかもと思いつつ、やっぱり悩ましい箇所が残りそうな予感。

まず、メソッドに関しては、機械的に visibility で分けられる気がする。

IDE の structure 表示とかで対応できたらうれしいのだが、public とそれ以外しか表示分けできないので無理。また、テキストベースでも visibility によって分離されたものが見たいので、そもそもダメっす。

val/var に関しては、backing field の存在しないものはメソッドと同様に扱えばいいと思われる。ただ、get() と set() の visibility が異なる場合があるので結構悩ましい。これは、visibility の広い側に寄せる感じで妥協してもあまり困らない気がする。

単純な field の val/var に関しては、メソッドとは分けて配置してもよさそう。しかし、理由をうまく言語化できない、、、4。まぁ、もしも実際にやってみる機会があれば何かわかるんじゃないかなw

backing field を持つ val/var に関しては、これも field 側で扱えばいいのでは。get()/set() でロジックも入れられちゃうので微妙なんだけど、transformation とか observer 的なものがほとんどで、メッチャ obtrusive な処理を持つケースとか普通無いはずなので、実害が出るケースはあまり無い気がする。

で、field って、結局何やねんって考えると、よくある OO の教科書的には private な変数があって、public の add, sub メソッドがあって、カプセル化されてるぜ!的な感じで説明されそう。変数部分が primitive であれば、値自体は immutable でもあるので、外部のコードによって変更されることがないと保証できる。しかし、interface の型で受けたやつとかは immutability の判断が不可能なんだよね。そう考えると、クラスによる内部 field に対するカプセル化の責務って、クラス内のポインタへのアクセスをカプセル化するだけであって、ポインタの先の実体の内容が変更されることに関しては不干渉ってことですよね。っていうかそもそも不可知なので干渉しようが無い、、、。

ってことで、なんとなくモヤモヤが残りますが、とりあえず backing field とその周辺に関しては data ってことでひとまとめにしてお茶を濁して自分をダマしてみようw

クラス内部のコードを visibility 毎に region 切るとしたら以下のような感じでしょうかね、、、。

// field の declaration と assignment を同時に行う場合、順序が振る舞いに影響するケースが出てくる。
// その場合、field への assignment をすべて単一の init block にまとめれば対応できそう。※ それでも `by` を使う際に無理なケースとか出てくるかな?
class Xxx {
  // region: public data  ※backing field とその周辺。backing field の無い val/var は method 扱い。fields and their surroundings 的な感じ。
  // region: internal data
  // region: protected data
  // region: private data
  // region: public method/class/interface
  // region: internal method/class/interface
  // region: protected method/class/interface
  // region: private method※ init block も含む
  companion object {
	  // region: public data  ※backing field とその周辺。backing field の無い val/var は method 扱い。fields and their surroundings 的な感じ。
	  // region: internal data
	  // region: protected data
	  // region: private data
	  // region: public method/class/interface
	  // region: internal method/class/interface
	  // region: protected method/class/interface
	  // region: private method※ init block も含む
	}
}

なんだかなー、、、。

  1. ある程度クラスを俯瞰する必要がある場合の話。Stack trace から辿って単純なバグをつぶすだけのような場合はそこまでしないこともある。

  2. Kotlin の coding conventions の Class layout にて 3. Method declarations と書かれていたので、関数ではなくメソッドとした。

  3. 単方向制約がつかない場合もあるが、それはおいしそうなスパゲティである。

  4. これは TODO ですね。

2
1
2

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
2
1