
再利用性の高いクラス作成に重要な“アクセス制御”
株式会社ガリレオ
小山博史
2010/1/28
これからプログラミングを学習したい方、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つ追加し、そのユーザー情報を表示する簡単な処理を記述しています。
sample14/app1/UserInfo.javapackage sample14.app1;
class UserInfo {
String name;
String eMail;
}
sample14/app1/UserInfoManager.java
package sample14.app1;
class UserInfoManager {
UserInfo[] userInfoArray = new UserInfo[10];
}
sample14/app1/App.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);
}
}
App.javaの実行結果
taro
taro@example.jp
■ クラスがクラスの実装に強く依存
これまで、こういったクラスをよく作成してきましたが、このままでは、「UserInfoManagerクラスとAppクラスは、UserInfoクラスの実装に強く依存してしまっている」という問題点があります。これについて、少し考えてみましょう。
現在のUserInfoManagerクラスは、UserInfoの配列を使っていて、10名のユーザー情報を管理できます。これを固定ではなく、10名より多くのユーザー情報を管理できるようにしたい場合は、java.util.Listなどを使います。
例えば、次のように修正します。java.util.Listインターフェイスの実装としてjava.util.ArrayListを使っています。ここでは説明の都合上、パッケージを分けてソースコードを提示しています。このため、パッケージ名も変わっていますので、注意してください。
sample14/app2/UserInfoManager.javapackage sample14.app2;
class UserInfoManager {
java.util.List userInfoArray = new java.util.ArrayList();
}
この変更は、Appクラスに影響があるため、Appクラスは下記のようなコードに修正する必要が出てきます。mainメソッド内で修正が必要となります。
sample14/app2/App.javapackage 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.app1.UerInfoManagerクラスとsample14.app1.Appクラスは一緒に使う必要があります。また、「sample14.app2.UerInfoManagerクラスとsample14.app2.Appクラスは一緒に使う必要がある」ことになって、クラスの再利用がしにくい構造となります。できれば、UerInfoManagerクラスの内部で、採用しているデータ構造に依存しない形でAppクラスを実装したいところです。
■ 依存しないようにするには
こんなとき、例えば下記のようにUserInfoMangerクラスとAppクラスを用意し、Appクラス側ではUserInfoMangerクラスが提供するメソッドを使って処理を記述すれば、UserInfoMangerクラスの内部で採用しているデータ構造に依存しなくなります。
sample14/app3/UserInfoManager.javapackage 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/App.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));
}
}
このため、UserInfoManagerを下記のように配列を使うように変更しても、Appには影響がありません(説明上区別が付くようにパッケージ名をsample14.app3aとしていますが、sample14.app3.UserInfoManagerの実装を置き換えられます)。
sample14/app3a/UserInfoManager.javapackage 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;
}
}
ここまでで分かるように、「各クラスのフィールドへ直接アクセスをせずに、メソッドを経由して必要な情報をやりとりする」設計が、基本となります。しかし、実際にはそう上手くはいきません。次ページで、その理由を説明し、さまざまなJavaのアクセス修飾子やアクセサメソッドについて解説します。
| Index | ||||||||
|
||||||||
【改訂版】Eclipseではじめるプログラミング バックナンバー 連載インデックスへ»
- 第1回 Eclipse 3.4で超簡単Javaプログラミング基礎入門
- 第2回 Javaで一から理解するプログラムの変数と演算子
- 第3回 プログラミングの醍醐味! Javaで“条件式”を理解する
- 第4回 プログラミングの真骨頂! Javaで“反復処理”を覚える
- 第5回 データ集合を扱うのに便利なJavaの配列と拡張for文
- 第6回 複雑なデータを表現できるクラスやフィールドって?
- 第7回 クラスの振る舞いを表すJavaの“メソッド”とは?
- 第8回 Javaの参照型を文字列操作で理解して文法を総復習
- 第9回 プログラムを「変更」しやすくする“インターフェイス”
- 第10回 Javaの実案件に必須のパッケージとインポートを知る
- 第11回 「static」でクラス共有の変数・メソッドを使いこなせ!
- 第12回 継承やオーバーライドで簡単にクラスを“拡張”しよう
- 第13回 “コンストラクタ”と初期化、本当に理解できてる?
- 第14回 再利用性の高いクラス作成に重要な“アクセス制御”
- 第15回 Javaは「抽象クラス」で実装を上手に再利用できる
- 第16回 “ネスト”した型で始める軽量Javaプログラミング!?
- 第17回 あなたの知らない、4つのマニアックなJava文法
- 第18回 強く型付けされているJavaの理解に必修の“型変換”
- 第19回 キュー構造をJavaで実装してジェネリック型を理解する
- 第20回 拡張for文の真の実力を知り、反復処理を使いこなせ
- 第21回 7ステップで理解するJavaでの列挙型/enum使用法
- 第22回 いまさら聞けない「Javadoc」と「アノテーション」入門
- 第23回 プログラマの宿命! 例外とエラー処理を理解する
- 第24回 Javaの例外処理で知らないと損する7つのテクニック
| Java Solution全記事一覧 |
TechTargetジャパン
- 並列分散処理の常識をHadoopファミリから学ぶ (2012/2/8)
並列分散処理の課題やHadoopの長所/短所、そして短所を補うHadoop関連プロジェクトの構成や概要などを簡単に紹介 - WebLogicサーバ最新版「12c」の気になる4つの特徴 (2012/1/31)
久々にメジャーアップグレードしたJavaアプリケーションサーバについて、製品担当者に軽量インストーラなどの特徴を聞いた - GitHubをもっとソーシャルに使いこなすための7つ道具 (2012/1/23)
ソースコードホスティングのGitHub周辺で便利な新サービスが続々登場しているので、まとめて紹介しよう。特に連動クラウド「fluxflex」が注目だ - 新キャラ登場!スクラムやるならRedmineとALMinium (2011/12/26)
「黒板を“かんばん”にしてたら先生に怒られた(T_T)」「管理はPC内でやればいいのよ」「承知しました」
|
|
キャリアアップ
スポンサーからのお知らせ
- - PR -
イベントカレンダー
- - PR -
