テスト可視性とアクセス修飾子

レガシーコード改善ガイドを読んでいたのだが、どうにも引っかかって先に進めなくなってしまった。具体的には、P.185〜P.186のコラムです。以下引用。

影響とカプセル化
 オブジェクト指向でよく言われる利点の1つは「カプセル化」です。私は、本書で紹介している「依存関係を排除する手法」を何度も人に説明してきましたが、多くの人から、それによってカプセル化が壊れると指摘されてきました。それは本当です。依存関係を排除する手法の多くはカプセル化を壊します。
 カプセル化は重要です。しかし、なぜカプセル化が重要かという理由のほうがもっと重要です。カプセル化は、コードについて調査するために役立ちます。上手にカプセル化したコードでは、コードを理解するための追跡箇所は多くありません。たとえばコンストラクタのパラメータ化(394)のリファクタリングを行って依存関係を排除するためにコンストラクタに別の引数を追加すれば、影響について調査する追跡箇所が1つ増えます。カプセル化を壊すとコードの調査方法はより難しくなりますが、将来的にコードの説明となるような優れたテストを書ければ、コードの調査はより簡単になります。クラスにテストケースがある場合、より直接的にコードを調査するためにテストケースを利用できます。コードの振る舞いを把握するために新しいテストを書くこともえきます。
 カプセル化とテストによる保護は必ずしも対立しませんが、それらが対立する場合、私はテストによる保護を優先します。そのほうが、将来カプセル化を強めるために役立つ事が多いからです。
 カプセル化はそれ自体が目的ではありません。それは理解するための手段です。

レガシーコード改善ガイド (Object Oriented SELECTION) P.185-186

この説明が何を言わんとしたいのかがいまいち分からないのですよね。


カプセル化そのものが目的ではないということには同意するのですが、私はオブジェクト指向のこころの定義に基づき、あらゆる種類の情報の隠蔽を指してカプセル化という用語を使っています。そのため、クラスのユーザーから見える範囲を制限し、適切な使用方法のみを提供することもカプセル化の一種であると考えています。
ここで言うカプセル化は何を理解するための手段なのでしょう。
コード?クラスなどの使い方?それともそれ以外?
うーん、何か自分の考えとあっているような、少しずれているような……


また、テストによる保護を優先するということは、極論では全てのフィールドとメソッドをpublicにし、テストをしてしまおうと言っているようにも聞こえてしまいます。(繰り返しますが、あくまで極端に取った場合、です。)
では、どの様に適切にカプセル化とテストを一致させるべきなのでしょうか?
この本はレガシーコード「改善」ガイドなので、その辺りは記述されていないのかも知れませんが、正しいカプセル化とテストとは、いったいどの様なものかを説明して欲しいところです。


あとは、この本ではfinalやsealedなどのクラス拡張禁止命令は使用しないことを推奨していますが……、そもそも考えが違うんですよね。
「テストなどのために適切に拡張するから、その拡張手段を提供する」のか、「知らない人が使うと、変に拡張して処理が正常に機能しない可能性があるから、拡張を提供しない」のか。
個人的には、前者を選ぶなら全てpublicでも構わないわけで*1……適切な使用方法を提供するカプセル化がされたクラスの方が使いたいと思います。まぁ無論、適切なインタフェースなどが提供されていることが前提のお話ではあるかもしれませんが。
結局id:bleis-tiftが述べた*2通り、プログラマを信用する/しないの問題でもあると思います。
そもそも、知識のあるプログラマであれば外に出す処理に適切な出力/副作用を持たせていると思うので、そこまでアクセス修飾子が致命的になることは少ないとは思うのですが……、やはり難しい問題だと感じます。


適切なテストの書き方を綺麗に纏めた本はないものか……


レガシーコード改善ガイド (Object Oriented SELECTION)

レガシーコード改善ガイド (Object Oriented SELECTION)