堅牢性を獲得する章

2.2. フィールドの可視性

『目的』

フィールドのアクセス範囲に気を配って変更を最小限に食い止めましょう。

『Before』

フィールドには、public、protected、private、package-private の4種類の可視性があります。

public class Hoge {
    private int foo;
    protected int bar;
    public boolean barbar;
        :
}

たとえばHogeを継承したクラスがbarフィールドへ直接アクセスしようとするとこうなります。

public class HogeEx extends Hoge {

    private int method1() {
        return bar;
    }
    
    private int method2() {
        return bar + 1;
    }
    
    private int method3() {
        return bar + 10;
    }
        :
}

しかし、あちこちでフィールドにアクセスしていると保守が難しくなってしまいます。

なんらかの事情でbarの型をintからBigDecimalへ変更しなければならなくなったとしましょう。
そうするとその変更は HogeEx にまで及んでしまいますね。

public class HogeEx extends Hoge {

    private int method1() {
        return bar.intValue();      // 修正
    }
    
    private int method2() {
        return bar.intValue() + 1;  // 修正
    }
    
    private int method3() {
        return bar.intValue() + 10; // 修正
    }
        :
}

利用している箇所は全て直さなければなりません。
保守が大変です。

『After』

変更による影響はなるべく最小限にとどめたいですね。
そこで、アクセサメソッドの登場です。
アクセサメソッドとは、フィールドにアクセスするためのメソッドです。

フィールドは private にしてアクセサメソッドを用意します。

public class Hoge {

    private int foo;
    private int bar;        // private にする
    private boolean barbar; // private にする
        :
    /* 代わりにアクセサメソッドを提供する */
    protected int getBar() {
        return bar;
    }
    
    protected void setBar(int bar) {
        this.bar = bar;
    }
    
    public boolean isBarBar() {
        return barbar;
    }
    
    public void setBarBar(boolean barbar) {
        this.barbar = barbar;
    }
}

アクセサメソッドのメソッド名は通常「get変数名」「set変数名」とします。
boolean 値の場合は「get変数名」の代わりに「is変数名」とします。

利用側はアクセサメソッドを通してフィールドにアクセスします。

public class HogeEx extends Hoge {

    private int method1() {
        return getBar();
    }
    
    private int method2() {
        return getBar() + 1;
    }
    
    private int method3() {
        return getBar() + 10;
    }
        :
}

barの型をintからBigDecimalへ変更しなければならなくなったとしましょう。

public class Hoge {
        :
    private BigDecimal bar = new BigDecimal(0); // BigDecimal に変更
        :
    protected int getBar() {
        return bar.intValue();                  // BigDecimal を int に変換
    }
    
    protected void setBar(int bar) {
        this.bar = new BigDecimal(bar);         // int を BigDecimal に変換
    }
        :
}

Hogeのbarを変更しても、利用側から見るとgetBar()は相変わらずintを返しますから 何ら変わっていません。
setBar()も同様にパラメータの型はintのままです。 つまりHogeExは、Hogeが変更したことを意識することはない(変更されたことすらも気づかない)ので、 何も手を入れなくて済むということですね。

「実装の詳細を隠蔽する」といいます。

『まとめ』

フィールドには(できればフィールドが定義されるクラス自身のメソッドからも)直接アクセスせずに、 代わりにアクセサメソッドを使いましょう。 また、アクセサメソッドはなるべくprotectedにするよう努力しましょう。

例には登場していませんが、もうひとつpackage-privateがあります。 package-privateは修飾子をつけない場合のデフォルトの可視性なのでデフォルトとも呼ばれたりします。
これは継承関係がなくても、同じパッケージのクラスからはアクセス可能というものです。 パッケージ内のみで使用するようなユーティリティクラス等はpackage-privateにしましょう。

コードを変更する時の影響範囲が最小限になるので保守性が向上します。

< 前のページへ

Pagetop