カプセル化と関心の分離とMVC:なぜ「Setter禁止」が推奨されるのか?
Javaなどのオブジェクト指向言語の学習を進めていると、必ずと言っていいほど「カプセル化」という概念に出会います。技術書では「値を隠蔽しろ」「Setterを作るな」と書かれていることが多いですが、MVCモデルで開発していると「結局Getter/Setterがないとデータが渡せないよね?」と疑問に思うことはないでしょうか。
良いコード/悪いコードで学ぶ設計入門 の中で取り上げられ、でもなーと思い、調べてみました。
本記事では、このモヤモヤを解消するために「カプセル化の真意」と「MVCにおける適切な役割分担」を整理します。
1. なぜ「Setter禁止」と言われるのか?
技術書で「Setterを作るな」と言われる理由は、「オブジェクトの整合性を守るため(不変性を保つため)」です。
もし公開されたSetterメソッドがあると、外部のクラスから値を自由に変更できてしまいます。すると、「年齢にマイナス値が入る」「必須項目がnullになる」といった、業務的にあり得ない状態を誰でも作り出せてしまいます。
カプセル化の真意は、「データを隠すこと」ではなく、「変更するロジックをクラス内に閉じ込め、常に正しい状態を保つこと」にあります。
例:あるべき姿への変更
❌ ダメな例:外部からステータスを直接書き換える
// 外部(Serviceクラス等)から値を直接操作
order.setStatus("SHIPPED");
✅ 良い例:状態の変化をメソッドとして定義する
// クラス内部でバリデーションを行いつつ状態を変更
public void ship() {
if (!this.canShip()) {
throw new IllegalStateException("発送可能な状態ではありません");
}
this.status = "SHIPPED";
}
2. MVCモデルとの折り合いをどうつける?
「MVCモデルではGetter/Setterを使わざるを得ない」というのは、結論から言うと「半分正解で、半分は誤解」です。これは、そのクラスが「何を目的としているか」によって使い分けるべきだからです。

役割分担の指針
| クラスの目的 | 役割 | Setterの扱い |
|---|---|---|
| DTO / Form | 画面入力やAPIのやり取り用。単なるデータの運び屋。 | あってもOK |
| ドメインオブジェクト (Entity) | 業務ロジックそのもの。整合性が非常に重要。 | NG(メソッド化すべき) |
補足:用語解説
DTO (Data Transfer Object)
データを運ぶためのオブジェクトです。ロジックを持たず、値の保持のみを目的とします。
ドメインオブジェクト (Entity)
ビジネスの本質的なルール(「受注」「商品」「顧客」など)を表すクラスです。ここに業務上のルール(ロジック)を詰め込むのがドメイン駆動設計の考え方です。
3. Serviceクラスの本当の役割
では、ロジックをドメインオブジェクトに寄せていくと、Serviceクラスは何をするのでしょうか?
Serviceクラスは、**「複数のドメインオブジェクトを調整する監督」**です。
- リポジトリからデータを取得する(ドメインオブジェクトを生成)
- ドメインオブジェクトのメソッドを呼ぶ(
order.ship()など) - 結果をDBに保存する
Serviceクラスにロジックを書きすぎてしまうと「トランザクションスクリプト(手続き型)」になりがちです。「値の変更に伴うルール」はEntityへ、「全体の流れ」はServiceへという住み分けを意識することで、コードの保守性は劇的に向上します。
まとめ
- カプセル化の本質: データの隠蔽ではなく「整合性を保証すること」
- Setter: DTOのような単なる入れ物には使っても良いが、ロジックを持つEntityではメソッド名で「意味のある操作」を表現する
- 設計のコツ: 「この値は外から書き換えられても安全か?」を自問自答し、安全でないならSetterを禁止してメソッドに置き換える

