當客戶端用顯式型別檢查(chained
if-else、switch)取代多型呼叫——即使型別變化已封裝在階層中——就出現 Unexploited Encapsulation。
核心定義#
階層原本就用來封裝型別變化、讓多型自動分派。客戶端若繞開階層、改用顯式型別檢查,等於沒利用到既有的封裝。
副作用:
- 緊耦合:客戶端綁定具體型別;新增型別必須同步更新
if-else/switch。 - 遺漏導致 bug:忘了檢查階層中某個型別會在執行期出錯——多型機制下根本不會發生。
常見成因#
- OO 中的程序式思維:C/Pascal 不支援執行期多型,習慣以型別編碼搭配
switch/if-else,被帶進 OO 程式碼。 - 未確實掌握 OO 原則:知道有階層與多型,但不會用它們建構高品質設計。
範例:JDK DataBuffer 階層#
JDK 的 DataBuffer 抽象類別有 DataBufferByte、DataBufferDouble、DataBufferFloat、DataBufferInt、DataBufferShort、DataBufferUShort 等子類別,分別包裝不同原始型別的資料陣列。同時 DataBuffer 也定義常數 TYPE_BYTE、TYPE_DOUBLE、TYPE_FLOAT 等。

Figure 4.17: 對 DataBuffer 階層使用顯式型別檢查
在
java.awt.image套件中,這些常數被廣泛用於switch顯式型別檢查:
ComponentColorModel有 16 個 switchRaster有 13 處DirectColorModel有 11 處BandedSampleModel、SampleModel、ColorModel等也都有
switch (transferType) {
case DataBuffer.TYPE_BYTE:
byte bdata[] = (byte[])inData;
pixel = bdata[0] & 0xff;
length = bdata.length;
break;
case DataBuffer.TYPE_USHORT:
short sdata[] = (short[])inData;
pixel = sdata[0] & 0xffff;
length = sdata.length;
break;
case DataBuffer.TYPE_INT:
int idata[] = (int[])inData;
pixel = idata[0];
length = idata.length;
break;
default:
throw new UnsupportedOperationException(...);
}transferType 是 int,switch 完全靠編碼的型別常數——明顯未利用既有的 DataBuffer 階層。
建議重構#
把分支動作搬進階層,改用多型方法。
範例的具體重構#
ColorModel中的protected int transferType改成protected DataBuffer dataBuffer。- 在
DataBuffer階層中為各子類別定義對應的getPixel()與getSize()。 - 整段 switch 縮減為兩行:
pixel = dataBuffer.getPixel();
length = dataBuffer.getSize();客戶端建立
ColorModel時,需依輸入決定要傳入哪種DataBuffer——這個 switch 集中到 client 或 factory 一處即可,而非散落整個程式碼。
軼事:被組織結構逼出的型別檢查#
一家電信公司中,
Pulse與Phone階層(CellPhone、LandPhone)原本透過多型計算費率。後來新需求要求CellPhone計算上網費——但Phone階層由 Device Team 擁有、Pulse由 Billing Team 擁有,兩隊跨大洲分布。Device Team backlog 太多無法及時改動,Billing Team 只好在Pulse中對Phone做型別檢查直接呼叫CellPhone的方法。
兩個教訓:
- 組織結構會把壞味道擠進設計——這也是 Conway’s Law 的具體呈現。
- 緊急工作流的快速妥協必須留下後續清債的書面追蹤,否則永遠回不來。
對品質屬性的衝擊#
- 可變更性與可擴充性:型別變動時所有 switch 都要改。
- 可重用性:行為散在客戶端,型別本身不再是「自足的可重用單位」。
- 可測試性:相同檢查程式碼複製多份,測試成本上升。
- 可靠性:忘了某個型別檢查 → 執行期錯誤。
別名#
- Simulated polymorphism:用條件檢查模擬多型。
- Improper use of switch statement:以 switch 取代多型分派。
- instanceof checks:程式碼中 instanceof 過度集中。