自分向け。
過去に作ったものは、この考えに則っていないものは多々あるが、あくまで「今」の考え。多分今後も変わり続けると思う。
継承か委譲か
継承にするか、委譲にするか迷った時の判断基準
継承しているクラスのメソッドを拡張していないんだったら委譲にする
継承を使ったらコード量は減るけど、機能追加をするときに継承元のクラスの実装とかフィールドを見ないといけない。見ること自体はどっちにしても見るべきとは思うけど、数日経ってから「なんだっけ、このthis.hoge
って?、となったりする。
もっとちゃんとした理由は調べれば色々あったりするけれど、こんな理由もあるというメモ。
委譲とインターフェースで余計な操作は公開しない
実装もありつつ統一操作も持たせたい時には委譲したクラスの操作で共有したい操作のみインターフェースで定義する。 それって抽象クラスと同じで、単に委譲実装をするから手間は増えるだけに感じるけど似て非なる実装。 なお、委譲の実装はIDEの機能を使えば そこまで大変ではない。
抽象クラスを使用しないことにムキになっているなっているわけではなくて、抽象クラスは基本的にテンプレートパターンのような「拡張」を前提とするものに限定しておいた方が良いのではないかな、というのが現時点の整理。
インフラやシステム環境のAPIにはラッパーを準備する
インフラやシステム環境は、後々、横断的な対応をしたくなることはよくある。テストの時にもモック化しやすい。過去の経験では日付や時間は操作できるようにしておく方が良い。少なくとも営業経過日のテストや日替わりの時刻変更をしなくて良かったケースは皆無だった。
継承を疑う条件
継承したクラスが継承元にはない
public
なメソッドを持っている時点で、一回疑うべきである。
カプセル化
カプセルは多少緩くても良い気がする
カプセル化としてフィールドやメソッドをprivate
にする作法がある。むやみに拡張をさせたくないときに、そのようにしましょう、という作法という理解。意識せず機械的に作法的にそうしているケースもある。
ただ最近、多少緩くても良い気がするように段々となってきた。勝手に使用したり、拡張されてほしくないケースもあると思うが、それを開発時にすべて想定するのは難しく、だからカプセルに閉じ込める、というのは利便性が下がるように思い始めているから。
ということで、最近のメソッドにはprivate
ではなくdefault
(無印)を使うようにしている。一応、フィールドは更新をされると想定外の挙動になる可能性があるのでfinal
で更新不可にはしている。
おそらく正しい作法としてはインターフェースと実装をペアにしてコンポジションなりを出来るようにしておけば、それが一番良いのだろうとは思う。でも、ちょっと全部にそれをするのはシンドイ。。
内部実装が漏れるのは良くない、というのは分かるけど、特にフレームワークなど基底層の場合「デバッグなどで必要としている情報をクラスが保持しているのは分かったけど、アクセッサが無くて参照できない」ということに何度か遭遇すると「更新はしないから参照はさせて・・」と言いたくなることが多々あった経験から。当然だけれど、そういう操作については、ドメイン層のクラスから直接参照するのはNGだと思うので何かしらのラッパークラスを設けて必要最低限の操作のみを開放するという作法は実施する。
インフラ層(フレームワーク含む)をドメイン層から直接使用しないようにするのは、実装による使用不可にするというよりも、動的・静的解析によって使用禁止喚起することをプロジェクト毎の基準を持って実施するのが良い塩梅では無いかな、と思う。
実際の現場はカオスなので、少しでも開放すると既得権益となってしまって済し崩しで設計思想が破壊されてしまう可能性が十分あるので、なかなかそうもいかないというのは分かるけど、とりあえず自分の開発では多少緩くするのが現在の指針。
不変の作法
フィールドの不変の作法
- プリミティブ系:
final
List
系:Collections.unmodifiableList()
Map
系:Collections.unmodifiableMap()
インスタンス構築
new
よりもstatic factory
構築するインスタンスの確定を遅れさせることが出来るので、new
よりもstatic factory
。緩いカプセル化と同じく、どういう要件で拡張をしたくなるのか未来は分からない。未来に対しては寛容でいた方が、後々幸せな事が多い気がする。
実装時の注意としてはコンストラクタはprotected
にすること。そうしないとサブクラスが作成できないから。
参考
補足として、決して new
が嫌いという訳ではない。作成するインスタンスを確定させておきたい強い意志がある場合は、new
の方が良いと思っている。例えば独自の実行時例外。
パラメータ、必須はstatic factory
、任意はbuilder
new
よりもstatic factory
にする延長で、static factory
は必須項目、builder
で任意項目という整理をしておくと使用時に分かりやすい気がする。
ただし、builder
パターンは、パラメータの相反については表現しづらいので、任意項目だけれどstatic factory
が全くない訳ではない。static factory
であれば、相反は表現できる。
Builderパターン
2017/8/27追記
Effective Java の Builderパターンとその拡張 - がくぞーのメモ
私の認識が間違っていたかも。
命名
パッケージ名
クラス名
オブジェクト指向
おすすめオブジェクト指向練習方法 | サイバーエージェント 公式エンジニアブログ
コーディング
Null
2017/8/18 追記
- 戻り値がNullかもしれない場合にはOptional
Java 8 "Optional" ~ これからのnullとの付き合い方 ~ - Qiita
セキュアなコーディング
www.slideshare.net