Effective Java 第3版を読んだので、本書で紹介されていた全90項目のプラクティスを簡潔にまとめる。
https://www.maruzen-publishing.co.jp/item/?book_no=303408

本記事は第4章「クラスとインターフェイス」の項目について記載する。

第4章 クラスとインターフェイス

項目15 クラスとメンバーへのアクセス可能性を最小限にする

良い設計のコンポーネントとは、内部データと実装の詳細を他のコンポーネントから隠蔽できているかによる。
隠蔽が重要な理由は、コンポーネントを分離することにより、開発、テスト、 最適化、利用、理解、修正を容易にするからである。

Javaのアクセス制御機構は、クラス、インタフェース、メンバのアクセス可能性を指定する。
アクセス可能性は、アクセス修飾子(private、protected、public)により決定される。

良いコンポーネントは、各クラスやメンバをできる限りアクセスできないようにするために、最も低いアクセス修飾子を指定するべきである。

  • トップレベルのクラスやインタフェースはパッケージプライベートにするべき。
    パッケージプライベートにすることで、パッケージの公開APIでなくパッケージの一部の実装として表現できる。

  • パッケージプライベートのクラスが一つのクラスだけで使われているならば、そのクラス内でprivateのネストしたクラスにすることを検討するべきである。
    アクセス可能性をパッケージ内のすべてのクラスから、使っているクラスだけに低減することができる。

  • テストを容易にするためにクラス、インタフェース、メンバへのアクセス可能性を緩和することは問題ない。
    publicクラスのprivateメンバをパッケージプライベートにすることはOK.

項目16 publicのクラスでは、publicのフィールドではなく、アクセッサーメソッドを使う

publicなクラスは可変なフィールドをpublicにするべきではない。
なぜなら、フィールドに直接アクセスされるのでカプセル化の恩恵を受けることができないから。

フィールドはprivateとし、publicなアクセッサーメソッド(ゲッター)を提供するべき。

項目17 可変性を最小限にする

不変クラスは、設計、実装、使用が可変クラスより容易である。
よって、可変にすべき理由が無ければクラスは不変とすべきである。

不変クラスの5つの規則。

  • オブジェクトの状態を変更するミューテーターメソッド(セッター)を提供しない。
  • クラスを拡張できないようにする。(finalクラスとする)
  • すべてのフィールドをfinalにする。
  • すべてのフィールドをprivateにする。
  • 可変コンポーネントに対する独占的アクセスを保証する。(可変オブジェクトを参照したフィールドが存在する場合、クライアント側から参照されないようにする。)

コンストラクタは、すべての不変なフィールドを初期化した状態でオブジェクトを生成するべき。

項目18 継承よりもコンポジションを選ぶ

新たなクラスに既存のクラスのインスタンスを参照するprivateのフィールドを持たせる。
既存のクラスが新たなクラスの構成要素になる設計のことをコンポジションと呼ぶ。

項目19 継承のために設計および文書化する、でなければ継承を禁止する

項目20 抽象クラスよりもインターフェースを選ぶ

項目21 将来のためにインタフェースを設計する

項目22 型を定義するためだけにインターフェースを使う

項目23 タグ付きクラスよりもクラス階層を選ぶ

タグ付きクラスとは、インスタンスが2つ以上の特性を持っていて、その特性を表すためのタグフィールドを持つクラスのこと。

そういったクラスは、多くの欠点を持っている。

  • 冗長:複数の実装が単一クラスに集約されている。
  • 誤りやすい:誤ったフィールドを初期化するとプログラムは実行時に失敗する。
  • 非効率:特性を追加したら、すべてのswitch文にcaseを追加しなければならない。

そこで、サブタイプ化をしてクラス階層構造にすることを検討するべきである。

項目24 非staticのメンバークラスよりもstaticのメンバークラスを選ぶ

ネストしたクラスは、親クラスに対して仕えるために存在すべき。

ネストしたクラスは、4種類ある。

  • staticのメンバクラス
  • 非staticのメンバクラス
  • 無名クラス
  • ローカルクラス

親クラスのインスタンスへアクセスする必要がないメンバクラスを宣言する場合、staticを付与してstaticメンバクラスにするべき。

static修飾子を省略した場合、個々のインスタンスは親クラスへの関係のない参照を持つ。
そのため、参照の保存とメモリを必要としてしまう。

また、参照がなければガベージコレクションの対象になる場合、親クラスのインスタンスが残ってしまう可能性があること。

項目25 ソースファイルを単一のトップレベルのクラスに限定する

単一のJavaファイルに複数のトップレベルのクラスを書くと、コンパイラが予想不可能な振る舞いが起こる可能性がある。

複数のトップレベルのクラスを入れる場合は、staticのメンバクラスを使うことを検討するべき。