連載
» 2010年01月28日 00時00分 UPDATE

【改訂版】Eclipseではじめるプログラミング(14):再利用性の高いクラス作成に重要な“アクセス制御” (1/3)

これからプログラミングを学習したい方、Javaは難しそうでとっつきづらいという方のためのJavaプログラミング超入門連載です。最新のEclipse 3.4とJava 6を使い大幅に情報量を増やした、連載「Eclipseではじめるプログラミング」の改訂版となります

[小山博史,株式会社ガリレオ]

クラスの実装に修正が入ったときの影響

 これまでの連載で、いくつかのプログラムを作成してきました。あるオブジェクトのフィールドに、ほかのオブジェクトからアクセスしたり、あるオブジェクトのメソッドを、ほかのオブジェクトから呼び出したりすることにより、プログラム全体が動作するようにコーディングをしました。

 ここで、ほかのオブジェクトからアクセスさせたくないフィールドを用意したり、ほかのオブジェクトには使用されたくないメソッドを用意したりしたい場合があります。Javaには、そういった「アクセス制御」をするための、「public」「private」といった予約語があります。

 また、「クラスの実装である内部構造を外部へ、どれぐらい公開するか」により、そのクラスの実装に修正が入ったときの影響度が変わってきます。

 今回は、アクセス制御について解説をします。EclipseでJavaプログラミングを始める準備がまだの方は、連載第1回の「Eclipse 3.4で超簡単Javaプログラミング基礎入門」で準備をしておいてください。

ユーザー情報を管理するサンプルから考える

 ユーザー情報を管理するプログラムを作成しているとしましょう。先に「sample14.app1」というパッケージを作成して、このパッケージ内にUserInfoクラス、UserInfoManagerクラス、Appクラスを次のように作成します。

 UserInfoクラスは、フィールド「name」「eMail」を持ちます。UserInfoManagerクラスはUserInfo型の配列で、要素を10個持つフィールド「userInfoArray」を持ちます。Appクラスは、UserInfoクラスとUserInfoManagerクラスを使って、ユーザー情報を1つ追加し、そのユーザー情報を表示する簡単な処理を記述しています。

package sample14.app1;
class UserInfo {
    String name;
    String eMail;
}
sample14/app1/UserInfo.java
package sample14.app1;
class UserInfoManager {
    UserInfo[] userInfoArray = new UserInfo[10];
}
sample14/app1/UserInfoManager.java
package sample14.app1;
class App {
    public static void main(String[] args) {
        UserInfo u = new UserInfo();
        u.name = "taro";
        u.eMail = u.name + "@example.jp";
        UserInfoManager manager = new UserInfoManager();
        manager.userInfoArray[0] = u;
        System.out.println(manager.userInfoArray[0].name);
        System.out.println(manager.userInfoArray[0].eMail);
    }
}
sample14/app1/App.java
taro
taro@example.jp
App.javaの実行結果

クラスがクラスの実装に強く依存

 これまで、こういったクラスをよく作成してきましたが、このままでは、「UserInfoManagerクラスとAppクラスは、UserInfoクラスの実装に強く依存してしまっている」という問題点があります。これについて、少し考えてみましょう。

 現在のUserInfoManagerクラスは、UserInfoの配列を使っていて、10名のユーザー情報を管理できます。これを固定ではなく、10名より多くのユーザー情報を管理できるようにしたい場合は、java.util.Listなどを使います。

 例えば、次のように修正します。java.util.Listインターフェイスの実装としてjava.util.ArrayListを使っています。ここでは説明の都合上、パッケージを分けてソースコードを提示しています。このため、パッケージ名も変わっていますので、注意してください。

package sample14.app2;
class UserInfoManager {
    java.util.List userInfoArray = new java.util.ArrayList();
}
sample14/app2/UserInfoManager.java

 この変更は、Appクラスに影響があるため、Appクラスは下記のようなコードに修正する必要が出てきます。mainメソッド内で修正が必要となります。

package sample14.app2;
class App {
    public static void main(String[] args) {
        UserInfo u = new UserInfo();
        u.name = "taro";
        u.eMail = u.name + "@example.jp";
        UserInfoManager manager = new UserInfoManager();
        // ここから
        manager.userInfoArray.add(u);
        UserInfo u0 = (UserInfo)manager.userInfoArray.get(0);
        System.out.println(u0.name);
        System.out.println(u0.eMail);
        // ここまで修正
    }
}
sample14/app2/App.java

 こういった実装をしてしまうと、sample14.app1.UerInfoManagerクラスとsample14.app1.Appクラスは一緒に使う必要があります。また、「sample14.app2.UerInfoManagerクラスとsample14.app2.Appクラスは一緒に使う必要がある」ことになって、クラスの再利用がしにくい構造となります。できれば、UerInfoManagerクラスの内部で、採用しているデータ構造に依存しない形でAppクラスを実装したいところです。

依存しないようにするには

 こんなとき、例えば下記のようにUserInfoMangerクラスとAppクラスを用意し、Appクラス側ではUserInfoMangerクラスが提供するメソッドを使って処理を記述すれば、UserInfoMangerクラスの内部で採用しているデータ構造に依存しなくなります。

package sample14.app3;
class UserInfoManager {
    java.util.List userInfoArray = new java.util.ArrayList();
  
    void add(UserInfo userInfo) {
        userInfoArray.add(userInfo);
    }
    UserInfo get(int id) {
        return (UserInfo)userInfoArray.get(id);
    }
    String getUserName(int id) {
        return get(id).name;
    }
    String getUserEmail(int id) {
        return get(id).eMail;
    }
}
sample14/app3/UserInfoManager.java
package sample14.app3;
class App {
    public static void main(String[] args) {
        UserInfo u = new UserInfo();
        u.name = "taro";
        u.eMail = u.name + "@example.jp";
        UserInfoManager manager = new UserInfoManager();
        manager.add(u);
        System.out.println(manager.getUserName(0));
        System.out.println(manager.getUserEmail(0));
    }
}
sample14/app3/App.java

 このため、UserInfoManagerを下記のように配列を使うように変更しても、Appには影響がありません(説明上区別が付くようにパッケージ名をsample14.app3aとしていますが、sample14.app3.UserInfoManagerの実装を置き換えられます)。

package sample14.app3a;
class UserInfoManager {
    // 配列を使う場合
    UserInfo[] userInfoArray = new UserInfo[10];
    int current = 0;
  
    void add(UserInfo userInfo) {
        if (current < userInfoArray.length) {
            userInfoArray[current] = userInfo;
            current++;
        }
    }
    UserInfo get(int id) {
        return userInfoArray[id];
    }
    String getUserName(int id) {
        return userInfoArray[id].name;
    }
    String getUserEmail(int id) {
        return userInfoArray[id].eMail;
    }
}
sample14/app3a/UserInfoManager.java

 ここまでで分かるように、「各クラスのフィールドへ直接アクセスをせずに、メソッドを経由して必要な情報をやりとりする」設計が、基本となります。しかし、実際にはそう上手くはいきません。次ページで、その理由を説明し、さまざまなJavaのアクセス修飾子やアクセサメソッドについて解説します。

       1|2|3 次のページへ

Copyright© 2017 ITmedia, Inc. All Rights Reserved.

@IT Special

- PR -

TechTargetジャパン

Touch Barという新UIを得た「MacBook Pro」、プレゼント!

この記事に関連するホワイトペーパー

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。