カプセル化と情報隠蔽は違うヨ
しつこいかもしれんけど、カプセル化と情報隠蔽は違うってことを啓蒙するためにさらに書き足してみる。
昔、何かで読んだセキュアなクラスの書き方のひとつに final を使う方法が紹介されていた。インスタンス変数を final 宣言すれば、カプセル化してあるけど情報隠蔽はしていない状態になる。
カプセル化はしてるけど情報隠蔽していない。つまり、カプセル化と情報隠蔽は違うということだ。
public class Book { public final String title; public final String author; public final String isbn; public Book(String title, String author, int price) { this.title = title; this.author = author; this.price = price; } }
注意しないといけないのは、インスタンス変数がプリミティブ型や Immutable なオブジェクトに限って情報隠蔽しなくてもセキュアだという点。Mutable なオブジェクトの場合は外部から変更される可能性があるので、きちんと情報隠蔽する必要がある。
Immutable なラッパーを作ってインスタンス変数を公開する方法もあるけど、バグの原因になる可能性があるので避けたほうがよい。
public class Book { public final String title; public final String author; public final String isbn; public final Date publishedDate; public Book(String title, String author, int price, Date published) { this.title = title; this.author = author; this.price = price; this.publishedDate = new ImmutableDate(published); } private class ImmutableDate extends Date { ImmutableDate(Date date) { super(date.getTime()); } public void setTime(long t) { // ignore } } }
この場合、publishedDate を取得したクライアントは Immutable とは知らないので、Mutable だと思って ImmutableDate の値を変更しようとする可能性がある。その場合、期待通りに動作しなくなるから。Mutable なクラスを継承して Immutable なクラスを作るのは、LSP(リスコフの置換原則)に反しているので正当な理由がある場合を除いてやるべきではない。