はじめに
ようこそ、ここへ!!
ここは、はじめてオブジェクト指向を勉強しようとしている人のためのページです。
ここの管理人は私です。よろしくお願い致します。
私は、オブジェクト指向は苦手ですが、みなさんと一緒に学びたく本ページを開設しました。
尚、プログラミング言語は、Javaを使用します。本ページは、ある程度Javaを知っている人向けになっています。 Javaを知らない人は、とりあえず、以下でJavaの雰囲気を掴んで、本ページへ戻ってきてください。
本ページは、はじめに『オブジェクトとは何か』から始まって、『デザインパターン』まで、わかりやすく解説していく予定です。
インデックス
- はじめに
- オブジェクトとは?
- プロローグ for オブジェクト脳
- 例題 for オブジェクト脳
- インターフェイスと抽象クラス
- デザインパターン
- 委譲・関連・集約
- クラス図もどき
- イテレータパターン
- アダプターパターン
- おわりに
コンテンツ
◆ オブジェクトとは?
オブジェクトとは?
オブジェクトとは、アイデンティティーがある『もの』と『概念』である。 なので、オブジェクトは、同種のものでも、アイデンティティーがあるので区別することができる。 そしてそこには、どの点で違うかという「属性」と、どの点で同種だと言えるかという「クラス」がある。
オブジェクト指向とは?
オブジェクト指向とは、「ソフトウェアの内部処理については考えずに、対象となる実世界について考えよう」ということ。 また、実世界の現象を説明するために単純化した仮説を立てることを「モデリング」といい、オブジェクト指向は「モデリング」をコンピュ ータ内でシミュレートすることによってソフトウェアを作り上げようとする指向。
オブジェクトの意味とは?
オブジェクト指向では最小単位は「オブジェクト」であり、それを説明するには「それは何か」を書かなくてはならない。
- 内包(主):「それは何か」を考える。
- 外延(従):「どれがそれか」を考える。
- 機能(従):「それができる事を」考える。
オブジェクトは内包が主である。 したがって、継承も内包の継承だけを使うべき、外延と機能は継承しない。
UMLとは?
単なる記法である。詳しくは以下を参照。
要求分析とは?
ユースケースで分析する。 あくまでも、どんな「要求」があるかを考える。「実現方法」は考えない。
ユースケース分析とは?
まずそのシステムを使う人の「立場(アクター)」を列挙し、 次に「アクター」がシステムを使って何をするのかを列挙する。
仕様分析とは?
仕様書とは「何をすればいいのか」を規定するもの、「それをどのように解決するのか」は考えない。 まずは解決すべき問題をはっきりさせる。
システム分析とは?
システム分析とは、システムが対象とする世界が「どうであるか」をモデリングすること。 「プログラムをどう設計すればいいか」とか「プログラムはどう動けばいいか」ということとは違う。
クラス図とは?
クラス図とは、プログラムを作るためのものではない。対象とする世界を理解するためのもの。
参考
本セクションは以下のサイトを大変参考にさせていただきました。
とにかく、すばらしいサイトですので、特にプログラマの方は必読です。
◆プロローグ for オブジェクト脳
ご挨拶
去年(2003年)の暮れから、『オブジェクト指向』を勉強してきましたが、とりあえず、インプットは完了しました。
これからは、アウトプットを徐々にして行きますので、宜しくお付合いお願い致します。
尚、以下のプログラムソースは、本:著者 牛尾 剛氏『オブジェクト脳のつくり方』を大変参考にしています。
私の拙い説明で分からない時は、是非、この本を購入して勉強してみてください。超おすすめです。
イントロダクション
COBOL,C,VB,Perl等のプログラム言語習得は、なんのカルチャーショックも無しに比較的容易にできますが……。 JAVAは違う、全然分からない、勉強しても分かった気がしない……。 しかし、『COBOL,C,VB,Perl等を知らない自分なんか、そんなJAVAは、無理や〜!!』と思わないで下さい。 プログラミングに固定概念がないあなたこそが、なまじC言語等を知っている人より、 マスターできる可能性があるのです。 事実、私は、固定概念があるため、大変苦労しています。
さて、JAVAは、なぜ、全然分からないのか?
答え:それは、『オブジェクト指向が分からないので、分かった気が全然しない』ということが、最近分かってきました。
COBOL,C,VB,Perl等のプログラム言語は、手続き型言語と云って、マシンに対して、 やりたい処理をフラットに記述して実現します。 それに対して、Javaは、オブジェクト指向型言語と云って、マシンなど意識ぜず、 やりたい処理を再利用を考慮して細分化し、細分化した小さな処理の組み合わせで、実現します。
細分化は、機能での細分化ではなく、役割での細分化とします。 そして、役割を演じるモノがオブジェクトとなります。 おぉ、分かったような、分からないような……。 とりあえず、能書きはこのぐらいにして、お題です。 おっと、そのまえに予備知識。
予備知識
[オブジェクト指向]
ソフトウェアの内部処理については考えずに、『対象となる実世界について考えよう』という指向。
そして、考えたモノを部品化して、その組み合わせで新たなプログラムを作りましょうという指向。
[オブジェクト]
考えて部品化したモノをオブジェクトという。 オブジェクトは、アイデンティティーがあり、同種の他のものと区別することができる。そしてそこにはどの点で違うかという「属性 」と、どの点で同種だと言えるかという「クラス」がある。 Javaでのオブジェクトとは、メソッドとプロパティからなり、クラスがインスタンス化したもの、Javaプログラムそのもの。
[クラス]
オブジェクトの元になるもの。要は、C言語で云うと、構造体そのもの宣言がクラスで、構造体型変数の宣言がオブジェクトみたいな 感じ。
[メソッド]
C言語で云う関数のようなもの。
[プロパティ]
C言語で云う変数のようなもの。尚、 プロパティ以外に、フィールドとも呼ばれる。
[プログラム]
Javaに関しては、クラス又はオブジェクトのどちらか。
[カプセル化]
オブジェクトの中身を隠し、直接アクセスを禁止して、メソッドを介して、アクセスしてもらうこと。
[継承]
すでに定義済みのクラス(親クラス)のメソッドとプロパティを引き継いだ新しいクラス(子クラス)を作ること。
[オーバーライド]
オーバーライドとは、継承で親クラスの機能を引継ぎますが、そこを子クラスなりの機能で置きかえること。
ちなみに、オーバーロードとは、クラスのメソッドで、引数の数や属性の違う同名のメソッドを定義することを云います。
また、ちなみに、オーバーライトとは、ファイルの上書きのことを云います。
これらは、イメージや語呂合わせが似ているので、間違わないようにね。
クラスのコーディング例
以下は、簡単なJavaのクラスのコーディング例です。クラスの雰囲気をつかんでください。
/**
* 人間を表すクラス
**/
public class Ningen{
private String name; // 名前
private int shincho; // 身長
/**
* 人間に魂を入れるコンストラクタ
* @param name 名前
* @param shincho 身長
**/
public Ningen(String name, int shincho) {
this.name = name;
this.shincho = shincho;
}
/**
* 食べる
**/
public void taberu(){
System.out.println(name + "は、沢山食べて、身長が" + shincho + "cm になりました。");
}
/**
* 寝る
**/
public void neru(){
System.out.println("身長:"+shincho + "cm の" + name + "は、寝るのが好き。");
}
}
/**
* 人間に指示するマネージャクラス
**/
public class Manager {
public static void main(String[] args) { // Javaのお約束
Ningen ningen = new Ningen("ひろ", 173);// 人間クラスに魂を注入し、人間オブジェクト誕生
ningen.taberu(); // 人間オブジェクトへ食べろと命令
ningen.neru(); // 人間オブジェクトへ寝ろと命令
}
}
ソース説明
public class Ningen{〜} は、 パブリックなクラスNingenです。
private String name;は、プライベートな文字列型の変数nameです。
private int shincho;は、プライベートな整数型の変数shinchoです。
thisは、thisが使われてるクラス自身そのものを指す。
public void taberu()は、パブリックなリターン値のないメソッドtaberuです。
System.out.println()は、画面に文字列を表示する。
public static void main()は、パブリックで静的でリターン値のないメソッドmainです。
/*〜〜〜〜*/は、ソースのコメントです。
◆例題 for オブジェクト脳
例題:『子供』がタオルを持ってくる
場面設定:
とある『父親』の『子供』は、『長男』と『次男』の2人でした。 『父親』は、お風呂に入るときにいつも、タオルを忘れます。 『父親』は、いつもお風呂からドア越しに、『子供』へタオルを持って来てと叫びます。 すると、とにかく、『子供』がタオルを持ってきてくれます。いい子やね。
オブジェクトの洗出し:
オブジェクト指向では、現実社会をシミュレートする指向ですから、オブジェクトは以下の様になります。
- 子供オブジェクト
- 次男オブジェクト
- 長男オブジェクト
- 父親オブジェクト
しかし、子供オブジェクトは、実は次男オブジェクト或いは長男オブジェクトを抽象化(次男も長男も子供)したものです。 このようなものは、まず、インターフェイスとしましょう。 そして、インターフェイスには、今回は『タオルを持ってくる』というメソッドの宣言のみ定義します。
/**
* 子供を表すインターフェイス
**/
public interface Kodomo {
/**
* タオルを持ってくる
**/
public void comingup();
}
つぎに、次男オブジェクトと長男オブジェクトは、子供インターフェイを継承して、 『タオルを持ってくる』というメソッドを実装します。
/**
* 次男を表すクラス
**/
public class Jinan implements Kodomo { // 次男は子供でもあります
public void comingup(){
System.out.println("次男がすぐタオルをもってくる。");
}
}
/**
* 長男を表すクラス
**/
public class Chounan implements Kodomo { // 長男も子供なので、継承してます
public void comingup(){
System.out.println("長男が嫌々タオルをもってくる。");
}
}
さて、父親クラスの、kodomo.comingup();ですが、子供オブジェクトに対して発行しています。 そして、子供オブジェクトへインスタンスしたクラスが『次男』のときは、次男オブジェクトのcomingup();が起動され、 『長男』のときは、長男オブジェクトのcomingup();が起動されます。 このように、kodomo.comingup();が一つにもかかわらず、ケースByケースで違うオブジェクトのcomingup();が起動されることを ポリモーフィズム(多相)といいます。
/**
* 父親を表すクラス。子供へタオルを持ってきてもらいます。
**/
public class Chichioya {
public static void main(String[] args) {
Kodomo kodomo = null;
if(args[0].equals("Chounan")) { // args[0]は1つめの引数
kodomo = new Chounan(); // 長男を子供に代入できる
}
if(args[0].equals("Jinan")) {
kodomo = new Jinan(); // 同じく次男を子供に代入できる
}
kodomo.comingup(); // 次男だろうが長男だろうが1つのコードでOK
}
}
今回の超ポイント:
次男オブジェクトと長男オブジェクトの共通部分であるcomingup();を、子供クラスに定義して抽象化しました。 つまり、子供インターフェイスではcomingup();の宣言のみで、実装は各々次男クラスと長男クラスで行いました。
一方、父親オブジェクトでは、子供オブジェクトに対してのみ処理をしており、次男オブジェクトと長男オブジェクトの実装をまった く関知していません。
このように、インターフェイスを使うことにより、オブジェクト同士の結びつきを弱くし、オブジェクトの独立性を高めます。 つまり、オブジェクトの部品化が促進されるわけです。
尚、抽象化に関しては、インターフェイスの他に、まさにそのもの抽象クラスがあります。
オブジェクト脳(何)とは、ずばり、インターフェイスと抽象クラスを使いこなして、オブジェクト同士の結びつきを弱くし、クラス の独立性を高め、部品化の促進を考えることができることだと思います。
インターフェイスと抽象クラス
インターフェイスと抽象クラスをまとめてみました。
Javaで云うインターフェイスとは、実装がないメソッドの集まりです。 (しかし、フィールド、クラス、インタフェースを含むことができる)
コーディング例:
public interface Kodomo {
/**
* タオルを持ってくる
**/
public void comingup();
}
抽象クラスとは、実装がないメソッドを1つ以上含むクラスです。
コーディング例:
abstract public class Kodomo {
/**
* タオルを持ってくる
**/
abstract public void comingup();
public void bakaoyaji_print(){
System.out.println("馬鹿オヤジ、またかよ!");
}
}
で、何がインターフェイスと抽象クラスで違うかというと、大きく分けて2つあります。
- 継承に関して、インターフェイスは複数継承ができ、抽象クラスは一つしか継承できない。
- メソッドに関して、インターフェイスは抽象メソッドだけで、抽象クラスは具象メソッドもOKです。
では、実際どのように使い分けるかというと。 抽象クラスは、親子関係のある子クラス達の共通役割(内容ではない)を定義するとき。 インターフェイスは、親子関係にかかわらず、共通役割(内容ではない)を定義するとき。
インターフェイスに関しては、パソコンのUSBコネクトをイメージすると分かり易いかも。 USB(インターフェイス)のあるパソコン(オブジェクト)には、 すべてのUSB機器(他のインターフェイス持つオブジェクト)をつな繋ぐ(代入)ことができる。
◆デザインパターン
デザインパターンとは、さて何でしょうか、辞書を引いてもまず出ていません。
デザインとパターンは、両方とも一般語で良く使いますが、デザインパターンとは……。
実は、単にデザインパターンといった場合は、GoFの23個のデザインパターンを指すみたいなのです。
では、GoFとは、何かというと『the Gang of Four』の略で、オブジェクト指向の達人の4人のことをいいます。
で、その達人4人が経験から編み出したプログラムを組む時の定石に、名前を与え、カタログとして整理し、1冊の本にまとめた物が『Design Patterns: Elements of Reusable Object-Oriented Software』で、そには、23個のオブジェクト指向でのプログラミングの定石が記されていたのです。
この本は、1994年の出版されましたが、日本人には、英語とオブジェクト指向は、なかなか難しく、そして、7年後の2001年に、それを分かり易く噛み砕いた、結城さんの『Javaで学ぶデザインパターン』が出版され、やっと、私のような凡人にも理解ができるようになってきたわけです。
まずは、23個のデザインパターンの概要です。
- Iteratorパターン
複数のオブジェクトに順番にアクセスする(1つ1つ数え上げる) - Adapterパターン
既存のクラスに別のインターフェースを持たせる(一皮かぶせて再利用) - Template Methodパターン
具体的な処理をサブクラスにまかせる - Factory Methodパターン
インスタンス作成をサブクラスにまかせる - Singletonパターン
あるクラスのオブジェクトを1個だけ作って共有する - Prototypeパターン
コピーしてインスタンスを作る - Builderパターン
複雑なインスタンスを組み立てる - Abstract Factoryパターン
関連する部品を組み合わせて製品を作る - Bridgeパターン
機能の階層と実装の階層を分ける - Strategyパターン
アルゴリズムをごっそり切り替える - Compositeパターン
容器と中身の同一視 - Decoratorパターン飾り枠と中身の同一視
- Visitorパターン
構造を渡り歩きながら仕事をする - Chain of Responsibilityパターン
責任のたらい回し - Facadeパターン
シンプルな窓口 - Mediatorパターン
相手は相談役1人だけ - Observerパターン
状態の変化を通知する - Mementoパターン
状態を保存する - Stateパターン
状態をクラスとして表現する - Flyweightパターン
同じものを共有して無駄をなくす - Proxyパターン
必要になってから作る - Commandパターン
命令をクラスにする - Interpreterパターン
文法規則をクラスで表現する
これからは、これらを、結城さんの著作権にはふれないように、自分の中で咀嚼し、解説していきたいと思います。
◆委譲・関連・集約
JavaやUML等のオブジェクト指向の本やWEBページには、
委譲(デリゲーション)、関連(アソシエーション)、集約(アグレゲーション)
等のキーワードが結構沢山出てきます。
初心者の内は、それどころでなく、気になりませんが、そのうちに、どのように違うのか、
どのように同じなのか、などと気になり始めます。
そこで、出る杭は打たれるでなく、出る前に杭を打ってしまいましょう。
- 委譲とは、あるメソッドの実際の処理を他のインスタンスのメソッドにまかしてしまうことをいいます。
- 関連とは、あるクラス1が、あるクラス2を使用することをいいます。
- 集約とは、特に『has-a』になる関連を集約といいます。
たとえは、ChichioyaクラスとChounanクラスがあったとします。 そして、Chichioyaオブジェクト内でChounanクラスのオブジェクトを保持しているとします。 コーディングをすると以下の感じです。
public class Chichioya {
public static void main(String[] args) {
Kodomo kodomo = null;
if(args[0].equals("Chounan")) { // args[0]は1つめの引数
kodomo = new Chounan(); // 長男オブジェクトを子供クラスに代入できる
}
kodomo.comingup(); // 子供(長男)オブジェクトのメソッドが起動できる
}
}
/**
* 長男を表すクラス
**/
public class Chounan implements Kodomo { // 長男も子供なので、継承してます
public void comingup(){
System.out.println("長男が嫌々タオルをもってくる。");
}
}
機能面からみると、外見は、 Chichioyaオブジェクトが、"長男が嫌々タオルをもってくる。" を表示していますが、内部では、ChounanオブジェクトへChichioyaオブジェクトが委譲しているのです。
クラス関係からみると、ChichioyaクラスがChounanクラスを集約して『has-a』の関係になっていることがわかります。 そして、このような関係を集約といい、関連しているといいます。
つまり、見る視点が異なるだけで、実は、この3つ(委譲・集約・関連)は、結果的には、ほぼ同じ事をいっているのです。
もしかしたら、間違った事をいっているかもしれませんので、識者の方、フォローよろしくお願い致します。
◆クラス図もどき
とりあえず、オブジェクト指向のおさらいです。
オブジェクト指向とは、対象となる実世界(モノと概念)について考えようという指向です。
オブジェクトとは、モノと概念です。
クラスとは、オブジェクトの雛型で、属性・操作・メッセージを持ちます。
さて、おさらいが終わったところで、デザインパターンといきたいところですが、 しかし、その前に、UMLのクラス図の習得がデザインパターン理解には有効ですので、 先にUMLのクラス図をご説明します。しかし、クラス図といっても、クラス図もどきです。ちゃんとしたヤツは、UMLの然るべき本やサイトで学んでください。
では、簡単にUMLを紹介すると、UMLとは、オブジェクト指向で設計をする時のモデリングの単なる表記方法で、9種類の図があります。特にクラス図が重要なので、今回は、クラス図だけを説明します。
クラス図のサンプル
たとえば、コンピュータの部品を部品棚にしまうことをモデリングしてみましょうか。
まず、部品クラスには、名前(name)と名前を取得するメソッド(getname() )があるとします。
これをクラス図で表すと以下のようになります。
+----------+----------+
| 部品 |
+---------------------+
| name |
+---------------------+
| getname() |
| |
+---------------------+
クラス図は、上記のように長方形を3つに区切り、上からクラス名(部品)、フィールド(name)、メソッド(getname() )を記述します。
そして、クラス間の関係は、関連・集約・継承の3つ(実際はもっとある)があり、以下の線で表します。 矢印は、使用する側のクラスから、使用される側のクラスへ向かいます。つまり、使用される側のクラスは、誰が自分を使用するかは関知しません。使用する側のクラスは、当然、知っているからこそ使用できるわけです。
Association: Aggregation: Inheritance:
関連 集約 継承
^ O #
| | |
<-+-> O-+-O #-+-#
| | |
V O #
+----------+----------+
| 部品棚 |
+---------------------+
| buhins |
| last |
+---------------------+
| addBuhin() |
| |
+---------------------+
O (使用する側)
|
|
V (使用される側)
+----------+----------+
| 部品 |
+---------------------+
| name |
+---------------------+
| getname() |
| |
+---------------------+
上記をJavaでコーディングすると以下のようになります。 尚、普通、コンストラクタは、クラス図には記述しません。
public class Buhindana{ //使用する側のクラス
private Buhin[] buhins;
private int last = 0;
public Buhindana(int maxsize){ //コンストラクタ
this.buhins = new Buhin[maxsize];
}
public void addbuhin(Buhin buhin){
this.buhins[last] = buhin;
}
}
public class Buhin{ //使用される側のクラス
private String name = "";
public Buhin(String name){ //コンストラクタ
this.name = name;
}
public void getname(){
return name;
}
}
尚、Javaソース関して、結城さんの『Javaで学ぶデザインパターン』を大変参考にしています。
◆イテレータパターン
いままで、オブジェクト指向を学んで、アプレット、サーブレッド、ウィンドウズAP、ドスAP等の初歩的なプログラムをJavaで組んできました。
そして、やっと今回、初歩的なプログラムを1歩踏み出したデザインパターンのイテレータを紹介する時が来たのです。
慣れないと、なにかと、チンプンカンプンなデザインパターンですが、これをマスターすると、マスター後がなにかとラクができるみたいなのです。
ですので、今、頑張ってマスターしてしまいましょう!
イテレータとは
イテレータとは、複数のオブジェクトに順番にアクセスし、それを1つ1つ数え上げることをいいます。
では、参ります。
イテレータの例題
とあるコンピュータの修理会社で、修理で使うコンピュータの部品を部品棚に入れて管理をしていました。
そして、部品棚に部品を格納し、その一覧リストを表示することになりました。
さて、ここで問題です。
部品棚に部品を格納し、その一覧リストを表示してください。
イテレータの例題の解答
まず、はじめにクラスを抽出してみましょう。
- 部品棚クラス
- 部品クラス
の2つは、すぐ思いつきます。 とりあえず、クラス図を書いてみましょう。
+----------+----------+
| 部品棚 |
+---------------------+
| addBuhin() |
| |
| |
| |
+---------------------+
O
|
|
V
+----------+----------+
| 部品 |
+---------------------+
| name |
+---------------------+
| getname() |
| |
+---------------------+
部品棚に部品を格納しなくてはならないので、部品棚クラスは、部品を格納するaddBuhin()メソッドが必要です。 また、格納(集める)することは、、部品クラスとの関係は集約です。
部品クラスは、とりあえず、名前フィールドと名前を取得する getname()を用意しましょう。
また、モノとしては、部品棚と部品ですが、部品棚の部品を走査して一覧をもとめる概念を部品イテレータとしてクラスとします。 この部品イテレータクラスは、部品を走査するメソッドnext()と、走査したとき次の部品があるかを見極めるメソッドhasnext()を持 ちます。
ここまで、整理するとクラスは以下の3つになります。
- 部品棚クラス
- 部品クラス
- 部品イテレータクラス
ここまでが、ごく普通のオブジェクト指向での設計です。
しかし、オブジェクト指向では、常に、再利用とシステムの成長を考慮して設計をしないとなりません。 再利用とシステムの成長を考慮しての設計とは、ずばり、クラスを抽象化してクラスの結びつきを弱くすることに他なりません。
ですので、集めた部品が数えられる部品棚クラスを抽象化した、『集めたある物が数えられる』というAggregateクラスを加えます。
また、部品を数える部品イテレータクラスを抽象化した、『あるものを数える』というIteratorクラスを加えます。するとクラス図は、以下のようになります。
+---------------------+ +-------------------+
| Aggregate |---------------->| Iterator |
+---------------------+ +-------------------+
| | | |
| iterator() | | hasnext() |
| | | next() |
| | | |
+---------------------+ +-------------------
# #
| |
| |
| |
+----------+----------+ +---------+---------+
| 部品棚 |<---------------O| 部品イテレータ |
+---------------------+ +-------------------+
| buhins | | aggregate |
| last | +-------------------+
+---------------------+ | |
| addBuhin() | | hasnext() |
| | | next() |
| | | |
+---------------------+ +-------------------+
O
|
|
V
+----------+----------+
| 部品 |
+---------------------+
| name |
+---------------------+
| getname() |
| |
+---------------------+
Aggregate と Iterator は、今回は、抽象クラスではなく、インターフェイスとしましょう。 なぜかというと、今回は、数えられるというインターフェイスと数えるというインターフェイスで充分だからです。
部品棚クラスは、Aggregateインターフェイスをインプリメントして、addBuhin() 等のメソッドを持ち、部品クラスを集約します。
部品イテレータクラスは、Iteratoreインターフェイスをインプリメントして、部品棚クラスを集約します。 部品クラスは、名前を持ち、それを得るメソッドgetname() があります。
コーディング
では、Javaでコーディングしていきましょう。
・Aggregateインターフェイス
public interface Aggregate {
public abstract Iterator iterator();
}
本インターフェイスは、『集めたある物が数えられる』ものなので、抽象メソッドのIterator()を定義します。
・Iteratorインターフェイス
public interface Iterator {
public abstract boolean hasNext();
public abstract Object next();
}
本インターフェイスは、『あるものを数える』というものなので、抽象メソッドの『次があるかを求めるhasNext()メソッド』と『次のあるものを走査するnext()メソッド』を定義します。
・部品イテレータクラス
public class BuhinIterator implements Iterator {
private Buhindana buhindana;
private int index;
public BuhinIterator(Buhindana buhindana) {
this.buhindana = buhindana;
this.index = 0;
}
public boolean hasNext() {
if (index < buhindana.getLength()) {
return true;
} else {
return false;
}
}
public Object next() {
Buhin buhin = buhindana.getBuhinAt(index);
index++;
return buhin;
}
}
本クラスは、Iteratorインターフェイスの抽象メソッドhasNext()とnext()を実装します。
・部品棚クラス
public class Buhindana implements Aggregate {
private Buhin[] buhins;
private int last = 0;
public Buhindana(int maxsize) {
this.buhins = new Buhin[maxsize];
}
public Buhin getBuhinAt(int index) {
return buhins[index];
}
public void addBuhin(Buhin buhin) {
this.buhins[last] = buhin;
last++;
}
public int getLength() {
return last;
}
public Iterator iterator() {
return new BuhinIterator(this);
}
}
本クラスは、部品を格納する配列を作成したり、逆に配列から部品を取り出したり、あと、イテレータを委譲しています。
この委譲ですが、これが、イテレータパターンのポイントになります。Aggregateインターフェイスから継承した抽象メソッドiterator()を実装します。この時、部品イテレータクラスのインスタンスを返すメソッドにします。
そして、 部品イテレータクラスのインスタンスがIteratorインターフェイス型で返されることに注目してください。
つまり、このiterator()メソッドを呼ぶ側にとって、部品イテレータクラスの存在は、知らなくてすむのです。
このようなテクニックによって、クラスの結びつきを弱くすることが出来るのです。
・部品クラス
public class Buhin {
private String name = "";
public Buhin(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
本クラスは、部品名と部品名取得メソッドを持ちます。
・メインクラス
public class Main {
public static void main(String[] args) {
Buhindana buhindana = new Buhindana(4);
buhindana.addBuhin(new Buhin("CPU"));
buhindana.addBuhin(new Buhin("メモリ"));
buhindana.addBuhin(new Buhin("マザーボード"));
buhindana.addBuhin(new Buhin("ハードディスク"));
Iterator it = buhindana.iterator();
while (it.hasNext()) {
Buhin buhin = (Buhin)it.next();
System.out.println("" + buhin.getName());
}
}
}
本クラスは、イテレータを使用したAPそのものです。 部品棚を作成し、そこへ4つの部品を登録し、イテレータを使って、部品棚を走査し、部品名の一覧リストを表示しています。
まとめ
普通、手続き型C言語等で上記を組むと、部品の入っている配列をforループで回して、部品名の一覧リストを表示します。
一方、オブジェクト指向型のJavaでは、なぜ、イテレータを使って、面倒くさく組むのでしょうか……。
大きな理由は、イテレータを使うことで、実装とは切り離して数え上げることが出来るからです。
それは、部品棚クラスのところですでにご説明した通りです。
つまり、オブジェクト指向では、クラスを抽象化してクラスの結びつきを弱くすることで、再利用とシステムの成長にたいして柔軟性を持つことができるのです。
◆アダプターパターン
前回、デザインパターン第1弾:Iteratorパターンを学びました。今回は、第2弾:Adapterパターンです。
普通、プログラムを組むときに、オブジェクト指向的には、やりたいことを抽象ベルで考えてから具象レベルに落とします。 しかし、新たに具象レベルのクラスを作るより、すでにあるそれに似た具象クラスの利用ができればラッキーです。 そんなラッキーなことを実現してくれるのが、Adapterパターンです。
Adapterパターンは、やりたいことを抽象クラスレベルで考えたTargetクラスと、やりたいことが似ている具象クラスのAdapteeクラスを、Adapterクラスで繋ぐデザインパターンです。
Client
+-------------+
| Main |
+-------------+
| |
+-------------+
| |
+-------------+
|
|
| 使用:関連
v
+-------------+ +-------------+ +------------------+
| Target | | Adapter | | Adaptee |
+-------------+ +-------------+ +------------------+
| |#-----------| adaptee |o---------->| |
+-------------+ 継承:汎化 +-------------+ 委譲:集約 +------------------+
| request() | | request() | | specialRequest() |
+-------------+ +-------------+ +------------------+
・例
たとえば、ある文字列を二段階で強調をしたい場合、抽象レベルでは、printEm()とprintStrong()の2つのメソッドを持つ抽象クラスを考えます。
つぎに、具象レベルでは、文字列を2種類以上で区別している、やりたいことが似ているクラスはないかと探します。 あった場合、アダプタークラスでそれらを繋ぎ(適合)、二段階での強調を実現します。
・ターゲットクラス(Target)
やりたいことを抽象レベルで考えたクラスです。
public abstract class Target {
public abstract void printEm();
public abstract void printStrong();
}
・アダプティ−クラス(Adaptee)
やりたいことが似ている具象クラスです。
public class Adaptee {
private String string;
public Adaptee(String string) {
this.string = string;
}
public void showWithParen() {
System.out.println("(" + string + ")");
}
public void showWithAster() {
System.out.println("*" + string + "*");
}
public void showWithSemicolon() {
System.out.println(";" + string + ";");
}
public void showWithPercent() {
System.out.println("%" + string + "%");
}
}
・アダプタークラス(Adapter)
ターゲットクラスとアダプティ−クラスを繋ぐ(適合)クラスです。 ターゲットクラスを継承し、アダプティ−クラスを委譲します。
public class Adapter extends Target {
private Adaptee adaptee;
public Adapter(String string) {
this.adaptee = new Adaptee(string);
}
public void printEm() {
adaptee.showWithParen();
}
public void printStrong() {
adaptee.showWithAster();
}
}
・メインクラス(Client)
クライアントクラスです。
public class Main { //client
public static void main(String[] args) {
Target p = new Adapter("Hello");
p.printEm();
p.printStrong();
}
}
Adapterクラスのインスタンスを抽象クラスのTargetクラス型へ代入しています。
そして、メソッドprintEm()とprintStrong()を起動すると、実は、回りまわって、Adapteeクラスの showWithParen()とshowWithAster()が実行されるわけです。しかし、メインクラスは、そんなことを知る由もありません。
このようにクラス間の結びつきを間接的にすることで、オブジェクトの独立性を高め、オブジェクトの部品化が促進されるわけです。
おわりに
どうでしたか、わかりましたか、オブジェクト指向は難しいので、説明するのも理解するのも大変です。 あせらず、徐々に学んで参ましょう。
とりあえず、デザインパターン23個全部の解説を目標に頑張って行きたいと思います。お付き合い宜しくお願いいたします。
尚、本ページを作成するにあたって、以下の本とページを大変参考にさせていただきました。ここに謹んで御礼申し上げます。