TSSI 基盤技術研修コース— プログラミング言語 — ＃ 2
久野靖∗

1999.5.14

はじめに• ただし、手続き型言語の系列もかなり変化してきている□ 第 1 回の内容について…

• その系譜を眺めてみよう• ご意見を拝見しましたが、結構「知っていた」方が多かったようです。まあ、体系的に整理するとか知1.1

らなかったことが多少でもプラスされればそれでよ
かったかと思います。

構造化プログラミング□ 初期の手続き型言語は GOTO 文化 (COBOL、FORTRAN、…)• あまり知らなかったとか難しかったというご意見もありましたが、全体的には適切なレベルだったかと• スパゲティプログラムになりがち思います。少々速い/内容が多いという感想もいただ □ 構 造 化 プ ロ グ ラ ミ ン グ (1960 年 代 末 〜) → GOTO
を使わず、もっと整った制御構造 (if-then、while、
きましたので気を付けます。

repeat-until) を使おう

• Java での例題を読んでいただく部分はやっぱり時間がかかりますね。ざっと説明してあとで再度各自に □ 段階的詳細化 (トップダウン開発)
読んでもらうという形を取るのがいいかと思います。
□ いずれも、教条的にやってもうまく行かないという結論□ 第 2 回の構成:□ 実現上の技術は、特にない (コンパイラとしては当たり前)• プログラミング言語の進化→前回の積み残し• オブジェクト指向言語の進化と諸概念を並行して見て行く (歴史的な順序におおむね従った方がわかりや

1.2

すい)

手続きと引数機構□ アセンブリ言語開発での手続き→広域変数に依存するこ• オブジェクト指向言語の実装技術→必要な箇所にそのつど挿入 (諸概念が出て来たところで説明した方がとが多かった□ 構造化設計 (モジュール間の存関係を減らす) →広域変

納得しやすい)数より引数• オブジェクト指向言語の利用技術→最後にまとめて□ 引数渡し機構…値渡し、名前渡し (Algol) →参照渡し、
解説
copy/restore (Fortran) →オブジェクト渡し (オブジ• Java による例題/演習→それに適した話題のところ
ェクト指向)に適宜挿入

1• 参照渡しの引数として渡した変数をそのまま作業変数としてさまざまな計算にも利用するのはよくない(なぜか?)

手続き型言語の進化

call subr(x)...
x = 1.0
y = .... x ....

□ 結局、現在でもメジャーな (プログラマ人口が多い、ソフトウェア製品の開発に使われることが多い) 言語は手続き型言語の末裔。
∗ 筑波大学大学院経営システム科学専攻

1

1.3

例外

Throwable ←例外すべて
Error ←エラー: 起こってはならない事象 (システムエラー等)
Exception ←普通の例外
RuntimeException: 通常の実行時にいつでも起こ
り得る例外
...

□ 例外とは→通常の制御の流れと違う制御の移行 (例外的状況/エラー)• 通常の制御構造でまかなえない goto の代替機能として…

□ 受け止める時→クラスを指定するとそのサブクラスの例
外も捕捉• エラー処理の統一的な枠組みとして…
□ Lisp などで古くからある→最近は C++、Java、…

try {
...
try {
...
※ここで例外が起きた時...
} catch(IOException e) { ... } ← IO 例外を捕捉...
} catch(Exception e) { ... } ←すべての Exception
を捕捉

try {
...
int i = Integer.parseInt(s).intValue();...
} catch(NumberFormatException e) { ... }□ 利点:• 同種のエラーを 1 箇所でまとめて処理できる□ 捕捉しなかった例外→外 (メソッド呼び出し元) に伝播• 異種のエラーをそれぞれ別の場所で処理できる• コードの主要部分がエラー処理でごちゃごちゃしない □ あるメソッドから例外が投げられるためには、メソッドの頭書きでその例外を投げると宣言しておかなければな• 手続きに無理矢理「エラーコード」を返させなくてらない。
すむ
public void read(...) throws IOException {...
}• 受け止められていない例外をコンパイラがチェック□ 例外機構のバリエーション• 効果:• 手続き内/手続き間あるメソッドを呼んだ場合に発生し得る (処理する必要がある) 例外は何と何かが明確になる• 例外の伝播…そのまま/汎用の例外に変換/必要なら□ あるメソッドを呼んだ場合に発生し得る例外は、必ず受変換
け止めて処理するか、または自分も同じ例外を返すよう• 例外からの復帰/後ろへ抜けるだけに宣言する必要がある□ 例外機構はどうやって実装する?

• 自明な選択肢:

手続きの戻りコードにして戻り側で検査→わかりやすいが通常の呼び/戻りがのろくなる→避けたい• 一般的な選択肢: 例外スコープの表を作っておき、例外が起きた時に自前で戻り処理をやりながら戻り先を検索する→例外が起きた時の速度は遅くなるが、通常の実行は遅くならない→例外はそう頻繁におき

public void myread(...) throws IOException {... read(...) ...
}または
public void myread(...) {try {... read(...) ...
} catch(IOExceptoin e) { ... }}• 効果: 処理すべき例外が処理されないまま終ることがないよう、言語機構としてサポートしてくれてないはず→よく使われる手法

いる

1.4

Java の例外機構□ た だ し 、す べ て の メ ソッド は 予 め「throws Error,

RuntimeException」と宣言ずみであるものと見なす□ 例外の種別→ Throwable クラスのサブクラスとして表す (分類を提供)• 理由: これらの例外はどこででも発生し得るため、いちいち宣言させていては大変すぎる• 共通のメソッドとして、printStackTrace() を提供□ 自前の例外を作る簡単な例題:• 次のような階層構造2• 範囲型、列挙型、集合型は現在の言語ではすたれている

public class Sample6 {public static void main(String args[]) {try {int i = (new Integer(args[0])).intValue();
System.out.println("sqrt(i) = "+mysqrt(i));} catch(Exception e) {e.printStackTrace();}}private static double mysqrt(int i)
throws Negative {if(i < 0) throw new Negative(i);return Math.sqrt((double)i);}}• 集合型の実装→ビットベクター。いくつかの主要な
Pascal 処理系では 32 ビットに限定されていたので「set of char」が使えなかった (今でも日本語だと無理がある…)□ C → Pascal と同時代のライバル• 範囲、列挙、集合に加え、論理型がなく、配列とポインタが混同されている (わざと) →アドホックな設計• 「int sub(int *a)」と「int sub(int a[])」の aの型は同じ? (Y/N)

class Negative extends Exception {int value;
public Negative(int i) { value = i; }public String toString() {return "Negative: <"+value+">";
}
}

1.5

• 「int a[100]」と「int *a」は意味は違うが…a の型は同じ?

□ 結局、型は何のためにある?

• 効率のよいコードを出すため…△ (ごく特別な場合)→ cf. ML の型推論
• 値の種別に応じた操作を人間がいちいち指定しなく
て済む…◎

型

□ 型とは?

• 値の種別の勘違いを検出してくれる (強い型の場合)• 値の集合→古典的な定義。例:
...}

…◎

整数型→ {1, 2, 3,• プログラムの構造を設計/記述する手段…◎• 一群の操作を提供する値の集合→抽象データ型/オブジェクト指向に合う。例:(Y/N)□ 強い型による保護

整数型→加算、減算、…

• 型検査で許された操作しかできない→メモリ保護と□ プログラミング言語における型…

して利用可。この方式の代表格:□ ごく初期の型

Java• 型検査を抜ける (escape) 方法があると役に立たない。代表: C や C++のキャスト• 機械語、アセンブリ言語 (原始的な場合) には型が• マシン語を直接書けばどのような操作でも可能なのないでやはり問題• (旧)Fortran →演算命令の区別のための型 (整数、実• Java では仮想マシン語の検証器によってこのような操作をはねる (ただし native メソッド --- 他言語/

数) →文字型がない、配列も「単なる繰り返し」であり型ではない機械語で書かれたコードをリンクして呼び出す ---• COBOL →レコード定義も「データの集まり」でありがあると無意味)型ではない• Algol68 → ref T 型 (C 言語でいう「*」) →ポンイ
ンタを型にした

1.6

モジュール

□ Pascal →これらさまざまなものを型として整理し、「ユー □ 手続きが単位では「カタマリ」が小さすぎる
ザ定義の型」を大幅に導入した。
□ 複数の手続き群が協調して 1 つのデータ構造を維持する• 基本型 (int、real、char、boolean、列挙型)→よくある• 部分範囲型 (「1..100」)□ これらの機能を言語機構として取り入れ→モジュール→• 配列型、ポインタ型、レコード型、集合型「データ構造と、一群の手続きとをひとまとめにして、外からアクセスできるもの/できないものを定める機能」

3□ 抽象データ型の実装方法…

• Modula、ConcurrentPascal、Modula-2、…
• C などでも「ファイル」はモジュール的に使える(static 変数/関数はそのファイル内だけで参照可能)• すべてヒープ上のデータ構造へのポインタとするな□ モジュールの機能は「隠すこと」。なぜ「不自由さ」が• スタック上のデータなどにしたい場合は、データ構

ら難しくはない (CLU)
造の大きさを知らなければならない→抽象データ型

重要なのか?

の定義において「外から見える」部分と「見えない」• 外部から「見えてしまう」と「使ってしまう」。
「使ってしまう」と「依存関係が増えてしまう」(それで後部分に分けて記述し、「見えない」部分は「見えないふり」をする→ Ada、C++→しかし「見えない」部分から変更しようとするとはまる)。

の変更でも再コンパイルが必要だったりしていまひ• たとえ変数が見えても「型定義が見えない」ように

とつ使いやすくない。現在のオブジェクト指向言語

できる言語も。そうすると「操作する方法がない」のではすべてポインタとするのが主流。

で「その型の変数は作れるが、そのモジュールの手続きを呼ぶ以外のことは何もできない」状態になる

1.8

がる考え。

□ たとえば上のようなコードで、格納される内容は int で• モジュールの実装→「隠すこと」だけだから特になし(コンパイラで正しくないアクセスをはねればよい)

1.7

型パラメタ

→情報隠蔽 (encapsulation) →抽象データ型につななくてもよいはず→「型」を「パラメタ」として扱える
ような抽象データ型 (型生成子) が使えるとよい。

• たとえば通常の言語の「配列」なども要素型をパラメタとする型生成子だと言える。

抽象データ型• ものによっては、パラメタ型が「どのような操作を□ 1 つのモジュールに 1 つのデータ構造、ではなく、あるモジュールが定義するデータ構造を「いくつも作りたい」

提供しているか」を指定する必要。

場合も→つまり、新しい「値の種類」が増える→型→「抽□ 型パラメタを持つ言語→ CLU、Ada、C++

象データ型」□ CLU による「任意の型 T の集合」型• 抽象データ型をサポートする言語→ 1970 年代に抽象データ型が脚光を浴びた時にいくつか作られた。
CLU、Alphard、…、Ada、…
• 現在ではオブジェクト指向言語にその機能が引き継がれていると言える (由来はちょっと違っているが…)□ CLU による「整数の集合」型
intset = cluster is create, insert, is_in
node = record[i:int, l,r:intset]
rep = variant[n:null, r:node]create = proc() returns(cvt)return(rep$make_n(nil)) end create
insert = proc(r:cvt, i:int)
tagcase r
tag n : rep$change_r(node$[i:i,l:intset$create(), r:intset$create()])
tag r(n:node) :if n.i = i then% do nothingelseif n.i < i then intset$insert(n.l, i)
else
intset$insert(n.r, i)
end
end
end insert...
end intset

intset = cluster[t:type]where t has lt:proctype(t,t) returns(bool)
is create, insert, is_in
node = record[e:t, l,r:intset]
rep = variant[n:null, r:node]create = proc() returns(cvt)return(rep$make_n(nil)) end create
insert = proc(r:cvt, e:t)tagcase r
tag n : rep$change_r(node$[e:e,l:intset$create(), r:intset$create()])
tag r(n:node) :if n.e = e then% do nothingelseif n.e < e then intset[t]$insert(n.l, e)
else
intset[t]$insert(n.r, e)
end
end
end insert...
end intset□ 型パラメタの実現方法• 自明な方法: コンパイル時にマクロ展開する→コードが大きくなる、厳密なチェックが抜けることがある、再帰的な型指定はできない (無限に展開)、その反面最適化はやりやすい。

4• オブジェクト指向プログラミング• パラメタ型の情報を実行時に保持する方法→利点と欠点は上記のほぼ反対。

• オブジェクト指向ソフトウェア工学 (分析、設計)• オブジェクト指向ソフトウェア開発 (コンポーネン□ 「配列」→ほぼすべての言語にある「組み込みの」型生ト技術)成子。たとえば Java では配列だけがこのような形で特• オブジェクト指向データベース (マルチメディア)

別扱いになっている。

• ここでは「言語」を中心に扱う

オブジェクト指向

1.9

2.2□ オブジェクト指向については、この後詳しく取り上げま
すが、ちょっとだけ。

オブジェクト指向的な考え方□ たとえば、「温室の温度調節システム」を考える。

□ オブジェクト指向の定義→「プログラムが扱う対象を• 旧来の考え方→「センサーを見て、気温が下がってきたらヒーターを通電するが、温度が上がりすぎた『もの』として取り扱う見かた/考えかた」• だから、「intset[real]$insert(s, 3.14)」では抽
象データ型だが、「s!insert(3.14)」と書ければそらヒーターを切る」「気温が上がってきたら窓を開く

れはオブジェクト指向、という考え方も成り立つ (CLU→制御する要素や条件が複雑になるとごちゃごちゃにそういう拡張を施したものがあった)。

になりやすい。

が、下がってきたら閉める」など機能中心に考える• オブジェクト指向→「気温センサ」「ヒーター」「窓開閉装置」などの「もの」を考える→「気温センサ」

この節のまとめ

1.10

は温度が低いと「ヒーター」、高いと「窓」に注意を□ アセンブラでも高水準言語でもプログラマの生産能力喚起→「ヒーター」は注意を喚起されると、定期的に(lines/日) は同じ→高水準であるほど効率はよい「センサ」に温度を尋ね、一定以下だと通電、十分暖
かいならヒーターを止めて仕事を終る→人間にとっ□ 考えなければならないこと (依存関係、関連性) が多いて考えやすく、適度な大きさに分けて考えられる。

ほど生産効率は悪くなる□ そのため、次のことが重要• 「かたまりの中」と「かたまりどうし」を分けて考える□ 開発されたのは 1960 年代半ば。最終版は Simula67• 見なくてよいことは「見えない」ようにすることが□ もともと「シミュレーションのための言語」→「もの」

大切を「まねする」しくみとしてオブジェクト指向を導入□ しかし現在のオブジェクト指向言語に見られる基本的なオブジェクト指向言語とその諸概念

2

Simula: オブジェクト指向言語の始祖

3• 大きな「かたまり」で考える

仕組みはすべて持っている (以下に列挙) →極めて先進的□ オブジェクト指向の基本的なアイデアは簡単

クラスによるオブジェクト定義□ 実際に言語として設計すると多くの考慮点

3.1□ 歴史的な推移に従って見ていくと分かりやすい□ オブジェクト:さまざまな「種類」がある (はず)。例:

「乗用車」

2.1

□ 1 つの種類のオブジェクト:

オブジェクト指向の定義複数ある (はず)。例:「私の車」「実家の車」□ オブジェクト指向とは、プログラムが扱う対象のそれぞ□ 「種類」を「クラス」(と呼ばれる単位) で記述し、「クれを自立した「もの」として扱う「考え方」

ラス」を雛型にインスタンス (個々のオブジェクト) を□ オブジェクト指向が取り入れられている分野はいろいろ

1 つ以上生成ある5□ クラスに規定されるもの

3.4

カプセル化の実現• インスタンス変数 (状態変数とも呼ぶ) →各インスタ □ カプセル化は単なる「スコープの問題」だからコンパイ
ンスの「状態」ないし「固有の値」を保持
ル時のみで処理• メソッド (C++用語では「メンバ関数」) →各インス• C++のように「キャスト」されてしまうと問題がある

タンスに付随する手続き。この手続きの中では、イ• 動的な (弱い型の) 言語では実行時にクラス情報を参ンスタンス変数の読み書きが可能。

照して検査□ Simula の用語ではインスタンスは「永続的なブロック」、
インスタンス変数は「ブロックの実行が終わっても値が3.5消滅しないようなブロックローカル変数」と位置付けて動的分配いた。しかし意味的には上記のように、現在のオブジェ □ 変 数 x に オ ブ ジェク ト が 格 納 さ れ て い る と し て 、
クト指向言語と同じ。
x.m(...) はメソッド m を呼び出す。
□ 変数 x がクラス A のインスタンスであれば、クラス A で3.2定義されているメソッド m が呼ばれる。クラス B のインクラスの実現方法

スタンスであれば、クラス B で定義されているメソッド□ クラスの実現はごく簡単→レコード型だと思って変数のm が呼ばれる。→どのメソッド m であるかは、実行時に割り当て等を計算すればよい。

x に格納されているオブジェクトのクラスに応じて動的に定まる→動的分配 (dynamic dispatch)• インスタンスの生成時にその大きさの領域をヒープから割り当てる• 「犬の『前進』、自転車の『前進』、バスの『前進』• 必要なら初期設定を行うはすべて実装としては別のものだが、機能としては同じに扱える」→ 1 つのコードで区別なく記述でき□ 変数の読み書きは領域先頭からの固定オフセットに対しるようにしたものて行えばよい• 動的分配がないと、「if 犬 then 犬. 前進 () elif

□ 動的な言語では変数の位置も探索する場合がある

自転車 then 自転車. 前進 () else ...

」となっ• 多くの変数があり、一部にしか値を設定しない場合には有利かもてしまう→コードがごちゃごちゃ、プログラマが一• 多重継承の場合にも有利 (後述)

分配は極めて強力な機能だと言える

時に考えることが増える。これと対比すると、動的• ただし、静的分配 (見ためは上と同じだが場所ごと□ 通常、動的分配のための手当てが必要 (後述)に型は決まっている) でもそれなりに使えると思う• 動的分配を使わないならデータ領域だけでよい (C++

□ 強い型の言語の場合、変数 x に対してメソッド m が使えなど)るかどうかは型検査でチェックされる→変数 x に実行時に何が入れられるかを規定しておく必要 (Simula の場

3.3

合どうかはすぐ次で説明)

カプセル化□ インスタンス変数の値は、そのインスタンスが持ってい3.6

るメソッドの中からしか読み書きできない→カプセル化

動的分配の実現• カプセル化によってインスタンス変数群に決まった □ 原理的には「メソッドへのポインタを各レコードに持たせる」
制約を持たせ続けることができ、外部からそれを破壊されないことが言語仕様上保障される□ 実際には 1 つのクラスに属するオブジェクトはすべて同
じメソッド群→クラスデータ構造にメソッドポインタを□ その後のオブジェクト指向言語では、外部からインスタ

持たせ、インスタンスにはクラスデータ構造へのポイン

ンス変数をアクセスできる「ようにも」指定可能に…(あタを持たせるのが普通。

まりよくないと思う)6• 共通部分とクラスごとの固有部分が持てる□ 各インスタンスに持たせると、メソッドを置換できる (実際にどれくらいそうしたいかは?)• メソッドポインタ部分はクラスごとに共通化できる□ 変数と同様、動的な言語では実行時に探すことも有り• 動的分配のない言語で動的分配をやるのは面倒で間得る

違いやすい (X Toolkit は今でもこれをやっている)□ 継承があると、さらに探索が必要な場合も□ C 言語でちょっと動的分配な例を書いてみました。

継承

3.7

typedef struct obj {char *name;void (*sound)(), (*pinfo)(); } *obj_t;
typedef struct obj_car {char *name;void (*sound)(), (*pinfo)();int speed; } *obj_car_t;
typedef struct obj_dog {char *name;void (*sound)(), (*pinfo)();char *kind; } *obj_dog_t;□ 継承とは→あるクラスから別のクラスに定義を「引き継ぐ」こと• 典型的には、インスタンス変数定義とメソッド定義(実現の継承)• しかし厳密な「継承の定義」をしはじめると難しい(後述)□ 継承は何が嬉しいか?

void car_sound(obj_t o) {printf("Broom!\n");}void car_pinfo(obj_t o) {obj_car_t c = (obj_car_t)o;
printf("name: %s, max_speed: %d\n",c->name, c->speed);}
obj_car_t new_car(char *s, int m) {obj_car_t c =(obj_car_t)malloc(sizeof(struct obj_car));c->name = s; c->speed = m;c->sound = car_sound; c->pinfo = car_pinfo;
return c;}void dog_sound(obj_t o) {printf("Vow! Vow!\n");}void dog_pinfo(obj_t o) {obj_dog_t d = (obj_dog_t)o;
printf("name: %s, kind: %s\n",d->name, d->kind);}obj_dog_t new_dog(char *s, char *k) {obj_dog_t d =(obj_dog_t)malloc(sizeof(struct obj_car));d->name = s; d->kind = k;d->sound = dog_sound; d->pinfo = dog_pinfo;
return d;}• 類似したクラス群を少ない記述で作成できる、定義の共有• 差分プログラミング• 共通の親を持つクラスのオブジェクトを総称的に扱える (なぜなら同じ変数群、同じメソッド群を持つから)(ただしそのためには動的分配も必要)• 強い型の言語では、子クラスの値は親クラスの型に互換→型の問題 (後述)
□ 例:

継 承 を 用 い た 構 造 化 グ ラ フィク ス (別 資 料

Sample8.java)

3.8

抽象クラス

□ 抽象クラス:機能の一部を子クラスの実装に任せるよ

うなクラス• その「子クラスに任されている」メソッドを抽象メ
ソッドという• Smalltalk-80 では、抽象メソッドは例外を投げることで示す• Java では、抽象クラス/抽象メソッドを宣言→コンパイラが検査main() {int i;
obj_t a[3];a[0] = (obj_t)new_car("my-car", 200);a[1] = (obj_t)new_dog("my-dog", "bulldog");a[2] = (obj_t)new_car("your_car", 300);
for(i = 0; i < 3; ++i) {obj_t o = a[i]; (o->sound)(o); (o->pinfo)(o);}}4

Smalltalk-80: 中興の祖

□ Smalltalk システム:
実行環境

7

Alto システム上の言語処理系+

• Alto: ゼロックス社の Palo Alto 研究所で開発さ □ 「メッセージ送信」とは要するにメソッド呼び出しのことれた「世界最初の」「パーソナルワークステーション」• オブジェクトが指定されたセレクタに対応するメソッである
ドを持たないときは、messagenotunderstood とい• Smalltalk に は い く つ か バ ー ジョン が あ る が 、うセレクタのメッセージに変換されて送り直される
Smalltalk-80 が一応完成された版(このメソッドは Object クラスで定義されている)• Alan Key らが Dynabook 構想の実現の一歩として開発した→自前でエラー処理したければこれをオーバライド• 並列性や分散性は Smalltalk-80 にはない。この面で拡張を行う研究は多数あり• ビットマップディスプレイ、対話的グラフィクス、サ
ウンド、ウィンドウシステムなど、当時の水準から見ると極めて先進的なシステム

4.3□ オブジェクト指向によるプログラミングの容易さがあっ□ コードブロック:てはじめて可能だった、とされている

コードの断片だが、それ自身オブジェ

クト。他の言語でいえば「クロージャ」に相当する□ 「中興の祖」という意味→ Simula にはじまるオブジェ• メッセージ valu(引数つきの場合は「value: 引数」
等) を送ると、そのコード内容を実行して return 文で指定した値を返す

クト指向言語について、世の中に再認識させた• プログラミング言語屋にとっては「センセーション」

z ← [x ← x + 1. ↑ x] value.
z ← [:n | x ← x + n. ↑ x] value: 100.

だった

• 多くの「見ため」は Apple の Lisa → Macintosh に引き継がれた (Smalltak-80 言語は引き継がれなか• ブロックはブロックの周囲の環境をアクセスできる(だからクロージャ)

った)

4.1

コードブロックの多用• 一方で、副作用だらけになるという問題点も□ Java では、コードブロックは無いがその代り「内部ク「純粋な」オブジェクト指向言語

ラス」や「無名の内部クラスが使える (後述)

□ 「純粋」という意味→すべてがオブジェクトである。たとえば整数や文字や論理値も (C++、Java 等ではこれら

4.4は基本型でありオブジェクトではない)

制御構造□ 制御構造もブロックとメソッドで構成• そのため、極めて統一的な言語仕様とできる (すべてのもののふるまいはサブクラスを作って改良可能)(x > 10) ifTrue: [x ← x - 1] ifFalse: [ ... ].
[x > 10] whileTrue: [....].

• たとえば「整数」のふるまいを変えたものも作れる□ そのほか「このような値が見つかるまで探す」といった• ただしリテラルがもとの Integer クラスのものな

指定にもブロックを利用→ Lisp 系の言語に近い (記号

ので…

型もある)

• コンパイラを変更してしまえば変えられる

4.5
4.2

特徴的な構文

先進的なプログラミング環境□ ウィンドウシステムがほとんど普及していない時期から□ すべては「メッセージ送信式」(と変数代入)

ウィンドウ環境だった□ クラスブラウザ、バックトレーサ、デバッガなどが組み• キーワードセレクタ型:

込まれた統合プログラミング環境だった

オブジェクト セレクタ.
valu ← aPoint x.
オブジェクト セレクタ: 引数 セレクタ: 引数 ….
anArray at: 10 put: x.

• ソースを追加/修正すると環境全体が変化していしまう→環境全体のダンプを取って保存 (もちろんソース単独でも保存とロードはできたが)• 演算子セレクタ型:• 全体的に言語、環境とも Lispっぽいと言える。cf.
InterLisp-D

オブジェクト 演算子 オブジェクト.
x ← x + 1.

8

4.6

MVC フレームワーク• (defflavor フレーバ名 各種情報…) で「クラス」
を定義□ 画面に見える「もの」(ウィンドウの内容や部品) を M/V/C• flavor のインスタンス→オブジェクトに分けて構築• Model:• (send オブジェクト セレクタ 引数…) → メッセー
ジ送信「もの」の状態を表すオブジェクト。たとえばスライドレバーであれば「現在の値」□ ここまでのところは Smalltalk-80 と本質的におなじ• View: 「もの」の状態を表示するオブジェクト。たとえばスライドレバーであれば、「レバーの絵」や「数値表示窓」

5.2

多重継承• Controller: 「もの」を操作するための動作を提□ Flavors による重要な拡張の 1 つ。flavor には複数の供するオブジェクト。たとえばスライドレバーであ親 flavor が指定できる→多重継承
れば「レバーをドラグする」「プラス/マイナス押しボタン」。
• 多重継承では小クラスは親クラスすべてからインス
タンス変数、メソッド群を引き継ぐ→「混ぜる」こ□ 1 つのモデルに対してビュー、コントローラは複数あっとによる干渉もあり使い方は難しいてよい (上の例)□ 実際には、「通常の (インスタンスを作る) クラス」と、□ その後の多くのグラフィカルなシステムにおいて MVC フ「他のクラスにまぜて機能を追加するクラス」(mixin ク

レームワークが採用されたラス) を区別して使い分けることが通例□ View と Controller を分離する必要はどれくらいあ
るか?

Java 2(Swing) などではこれらを一体化した• 例:

delegate というものを使用

ウィンドウクラスに対し、「窓枠をつける mixin

クラス」などを混ぜて機能の増えたウィンドウを作って行く• このような操作を mixin 操作と呼ぶ

Lisp 系のオブジェクト指向言語

5□ Smalltalk-80 は最初から Lisp によく似た側面を備え

5.3

ていた

メソッド結合□ Flavors で提案されたもう 1 つの重要な拡張。

• そのため、Lisp 屋は Lisp にオブジェクト指向を導• Smalltalk-80 では子クラスのメソッドは親クラス入することで Smalltalk-80 のようなよい言語/環境の同名メソッドを置き換え→親クラスのメソッドのを入手できるのではと考えた

動作「も」利用したい場合は「super セレクタ …」• 実際、多くの Lisp ベースのオブジェクト指向言語が作られたにより明示的に呼び出し• 多重継承では親が複数あるから上の方法ではいまいち• その際、Smalltalk-80 や Simula になかった新しい• C++では「どの親の同名メソッド」という形で呼べる概念も多く考案されたが、この方法で十分かどうかは?

□ 現在でも標準として残っているのは CLOS(Common Lisp

Object System) →ただし今後の Lisp はどれもオブジェ □ Flavors では、通常のメソッド (primary) のほかに、
deamon メソッド (before daemon、after daemon) がクト指向機能は持つようになる (CLOS 方式かどうかはある (実際にはもっといろいろあるがこれらが主に使わ分からないが)れる)

5.1□ 多重継承とメソッドのオーバライドがある状態では…、Flavors

次の順でメソッド群が呼ばれる□ Zetalisp (Lisp Machine System で採用した Lisp の• まず、before daemon が親クラスから子クラスへの方言) 上のオブジェクト指向機能。クラスのことを順で呼ばれるflavor と呼ぶ

9• primary method はこれまで通りのオーバライドな □ 10 年以上たって、ようやく「強い型のオブジェクト指向言語」が当たり前になった (C++が代表的)ので一番最近に定義された子クラスのものだけが呼ばれる• 最後に after daemon が子クラスから親クラスへの6.1

順で呼ばれる□ 何のためにこうなっている?

→ before daemon は「前強い型の概念と利点/弱点□ 強い型とは?

→ コンパイル時にすべての式や変数の型が定まっているしまつ」、after daemon は「後しまつ」を行い、それらは順番に結合されて各レベルのクラスの仕事を実行する• 利点:

コンパイル時検査、設計の手段□ 使いこなせば便利なのかも知れないが、やっぱり難しい• 弱点:

繁雑、めんどくさい???

(と思う)□ 中庸もある:

例 CommonLisp → 型はなくてもいいけ

ど、指定してもいい。指定すると効率がよいかも/コン

5.4

パイル時検査が可能

CLOS とマルチメソッド方式□ CLOS (Common Lisp Object System) → CommonLispの言語仕様のうちの、オブジェクト指向機能部分をいう(後から追加されたもの)

6.2

弱い型のオブジェクト指向言語□ 変数にも式にもコンパイル時の型はない□ 最大の変化→汎用関数 (generic function) に基づく

□ しかし、実行時には型 (==クラス) がある!!!

メソッド呼び出し• 「anObject message.」→「OK である」か「そのメ• Flavors: (send オブジェクト セレクタ …) →「どのメソッドか」は「オブジェクト」と「セレクタ」

ソッドはない!」かどちらか。

• 「そのメソッドはない」がどこで起こり得るかを予め知る方法はない→製品としてソフトを作るときにで決まっていた• 最初の引数 (レシーバ) のみを重視しすぎ? → 「すべての引数がメソッドの決定に関与する」

は弱点となり得る• 「OK である」ならよいのか?

(defclass X ....)(defclass Y ....)(defmethod method1 ((a X) (b Y)) ... *1 )(defmethod method1 ((a X) (b X)) ... *2 )...
(method1 anX anY) → *1 が呼ばれる(method1 anX anX) → *2 が呼ばれる→ たまたまそういう名前のセレクタが利用可能、だったらもっとたちが
悪い?

6.3

強い型のオブジェクト指向言語• 「メソッドがクラスに付属していない」「構文的には □ Simula が既にそうであった
普通の関数みたいな見え方」→特徴的 (好みも分か• 「型」と「クラス」は同じものとみなす (ちょっとはれる)
違うが)• 多重継承やメソッド結合は Flavors 以来引き継がれ• 「ある型の変数/式」は実行時に「そのサブクラスのている値も持つことができる」

6□ この規則は通常の「強い型の言語」からはだいぶ離れている

オブジェクト指向と型□ Simula

は 強 い 型 の 言 語 だった が 、そ の 後 、Smalltalk-80、Flavors、等 は す べ て 弱 い 型 の言語□ C 言 語 に オ ブ ジェク ト 指 向 を → や は り「 オ ブ ジェク
ト型」はすべて一緒、というタイプが多かった (例:Objective-C) →強い型ではない

10

Pascal ... x:integer := 1;x の型:integer、1 の型:integer
Java ... o:Object := new Integer(1);o の型:Object、式の型: Integer(Object のサブクラス)• Object 型には任意のオブジェクトが入れられてしまう• 代入の左辺と右辺の型は同じでなく包含関係→複雑さの原因• ただし、メソッドを呼ぶときは型が合わないとだめ

7.2

Smalltalk-80 のクラスとメタクラス□ Smalltalk-80 では「クラス」もまたオブジェクト

i:int := o.intValue(); ... ×
i:int := ((Integer)o).intValue(); ... ○• クラスに対してメッセージを送る→メソッドを追• このキャストは「実行時の型検査を伴うキャスト」でSmalltalk-80 で書かれているので当然といえば当然)

加したり修正したり等ができる (実行環境全体があって C のキャストとは違う (もともとのオブジェク
トが Integer ないしそのサブクラスでなければ例外□ クラスオブジェクトは Metaclass というクラスのインが発生)スタンス。たとえばクラス Collection のクラスオブ□ ある型に属するかどうかを判定→ instanceof 演算子

ジェクトは Collection class(という式でアクセス)。

Collection class は Metaclass というクラスのインスタンス

if(o instanceof Integer) ...

□ もっと自由に型の情報そのものを扱う→自己反映機能• Metaclass は各クラスオブジェクトを初期設定する
機能をおもに提供→ Metaclass を修正する (!!) と、

自己反映機能7□ 自己反映 (reflection):

Smalltalk-80 システム全体の動作が変化させられる (!!)

実行中のコードが、実行系の情報にアクセスしたり、実行系の状態/動作を変更したりできるような機能

7.3

メタオブジェクトプロトコル (MOP)• 狭い意味では前者のみ (後者を reification と呼ん □ 「オブジェクト」を統括する (ふるまいを定義する) オ
で区別することも)ブジェクト→「メタオブジェクト」(例: クラスに対してはメタクラス)□ 何のためにそんなことをするのか?
□ メタオブジェクトが提供するサービス、API →メタオブ• 例: デバッガ→実行系の内部状態を調べたり変更する必要

ジェクトプロトコル• 例: システムの拡張→「任意の手続き呼び出しを遠 □ 最近の多くの言語ではメタオブジェクトプロトコルを提供することで自己反映機能をさまざまに利用可能
隔メッセージに変換」など• 例:

拡張可能言語 (構文や意味づけ等)• CLOS: メソッド呼び出しの意味づけなどを自由に変
更可能

7.1

• OpenC++: コンパイル時 MOP →メタオブジェクトを定義すると、コンパイル時にメタオブジェクトがソー

3-Lisp: リフレクションの元祖□ ベースレベルとメタレベルを区別• ベースレベル:スを変更した上でコンパイル→言語の意味づけが変化させられる

通常の実行• メタレベル: ベースレベルの実行系の情報 (バインディング、継続等) が見える

7.4

Java の自己反映機能• メタレベルを変更→ベースレベルでの対応する状態
変化が起こっている→これにより、言語セマンティ □ Java の自己反映機能→処理系そのもを変更する、という部分はない。
クス (実行順序の制御等) が拡張可能• 内部の状態をのぞく□ 3-Lisp のさらに特徴→「メタレベル」はさらに「メタメタレベル」によって制御可能→無限の reflective

tower になっている• のぞいた情報を利用して、その場でメソッド呼び出し等を組み立てて実行させられる• 実装上は「遡られたところまで自動的にメタレベル □ 強い型の言語は「型が合わなければ扱えない」→リフレ
クションのような自由自在なことは表しにくいを用意」
11• Java ではこれらをきちんと型を割り当てた上で可能にしている

System.out.println("Result Class:"+(res.getClass()));
System.out.println("Result: "+res);} catch(Exception e) { e.printStackTrace(); }• ある意味では、Lisp 等の「eval」(任意のプログラ}ムを合成してその場で走らせる」を強い型の言語上}で可能にしたといえる}□ 任意のオブジェクトは getClass() でその Class オブ □ これをたとえば次のクラスに対して使ってみる…
ジェクトを取得できる
public class Sample9Test {int val;public Sample9Test() { val = 1; }public Sample9Test(int i) { val = i; }public Sample9Test add() {return new Sample9Test(val+1);}
public Sample9Test sub() {return new Sample9Test(val-1);}
public String toString() {return "Sample9Test("+val+")";}}• または、static メソッド Class.forName("...") でも□ Class オブジェクトはそのクラスに関する情報を取得するメソッドを持つ• 例:

getConstractors(), getMethods(),

getMembers()□ Constructor オブジェクトの newInstance() を呼ぶとオブジェクトが生成される□ Method オブジェクトの invoke() を呼ぶとメソッドが実行できる□ なお、この方法で通常取れるのは public なものだけ (セキュリティ上の制約)□ たとえば、任意のクラスを 1 つもってきてオブジェクトを生成しメソッドを呼ぶ (ただし引数はすべて空) というプログラムimport java.io.*;import java.lang.reflect.*;

8

継承と委譲

□ 継承 (inheritance):

Simula、Smalltalk-80 以来の「由緒正しい」やりかた
public class Sample9 {public static void main(String args[]) {• 問題点: サブクラスを作ると、その中では親クラス
BufferedReader br =new BufferedReader(の変数が自由にいじれてしまう→カプセル化の破壊
new InputStreamReader(System.in));• C++など→サブクラスでいじれる変数、いじれない変while(true) {数を区別可能に→それが問題の解決になってるのか
try {System.out.print("Class Name? ");どうか??
System.out.flush();String cname = br.readLine();□ 委譲 (delegation): 継承の代替案 (実装としての)if(cname.equals("")) break;Class cls = Class.forName(cname);
Constructor[] cons = cls.getConstructors();
8.1 継承の意味づけ
for(int i = 0; i < cons.length; ++i)
System.out.println(""+i+": "+cons[i]);□ 継承がやってることは何かというと…
System.out.print("Constructor Number? ");System.out.flush();• インスタンス変数とメソッドを引き継ぐ
int cno = Integer.parseInt(br.readLine());
Object obj =• その結果として、呼べるメソッドの集合や外から見cons[cno].newInstance(new Object[]{});
たオブジェクトの振る舞いを引き継ぐ
Method[] meths = cls.getMethods();for(int i = 0; i < meths.length; ++i)
• たとえば B が A のサブクラスであれば、「A のインス
System.out.println(""+i+": "+meths[i]);タンス」の代りに「B のインスタンス」を与えてもそ
System.out.print("Method Number? ");System.out.flush();のまま動く (建前としては)int mno = Integer.parseInt(br.readLine());• 動的分配の前提として、「A 型の変数に A のサブクラ
Object res =スがいろいろ入れられる」ということが必要
meths[mno].invoke(obj, new Object[]{});

12• 共通の親は統合する→「どこに親が埋まっているか」のポインタをそれぞれのインスタンスに埋めること□ しかしよく考えると、これは「実装の継承」が先にあり、その結果として「たまたまどれでも同じように取り扱える」ようになっているだけとも思える。この「たまたま」は気持ち悪いで対応□ 「共通の親は統合」でもう 1 つの素直なアプローチ:

最初に名前で探索し、その場所を覚える

8.2

継承の実装

□ ごく素直な継承の実装方法→オブジェクトの構造が重要。 8.5
インスタンス変数を定義された順に並べておく→子クラスでインスタンス変数を追加した場合は後ろにつけ加え

委譲: もう 1 つの実装

□ 継承は「親のインスタンス変数やコードを取り込んで来て自分の一部として実行させる」→カプセル化が壊れる

ていく

等の問題

• この方法であれば、クラス A のどのサブクラスでも □ 親の機能が必要なら、親を別のインスタンスとして持っA までで定義されている変数のオフセットは同一 →ていて、これを普通に呼び出すことでも利用可能→委譲
オフセットで直接アクセス可能(delegation) の考え方
• メソッドのコードがそのまま利用可能• 簡単に言えば、自分で実装しないメッセージを「たらいまわし」にする• 分かりやすく、効率がよい。ただし多重継承に対応できない□ 委譲のさまざまな利点

8.3

メソッド探索• カプセル化が壊れない• 委譲先を実行時に動的に切り替えることができる□ メソッド呼び出しで実際にどのメソッドが動くかはオブ• 多重継承の実装が用意 (多重継承の実装として、一部に委譲を使うことも)ジェクトのクラスによって変化→メソッド探索• Smalltalk-80 では、最初にそういう呼び出しがあったときに探索を行い、その情報をキャッシュに保持。
クラス構造が変化したときはキャッシュをご破算にしてやりなおす。動的にクラスが変化する環境ならでは8.6□ クラスがなく、「ひな型」のオブジェクトを複数コピーすることでインスタンスを作る• C++、Java などのコンパイルする言語では、分岐表を作ってそれに基づいて分岐すればよい。分岐表そ□ 継承ではなく委譲を使うのもののスロットも変数と同様にして管理可能

8.4

多重継承の実装□ 多重継承:Self: コピー方式の OOPL

• 委譲した場合でも「元のオブジェクト」を覚えておいて、自分自身に対してメッセージを送った場合元から探す。これがないと次のようなメソッド (抽象メソッド) が使えない
moveRight | n |self turn 90.
self forward n.

2 つの親から継承すること• 問題: 複数の親が共通の親クラスを持っていたらど□ Self 言語は「コピー方式の」「委譲に基づく」オブジェ
うするか?
クト指向言語が有用だという実証の意味が大。コードの□ C++の多重継承では 2 つの方式がサポートされているの性能も動的コンパイル (よく使う/高速な組合せだけをコンパイルしていく) などの技術により優れていた

で大変• 共通の親があったとき 2 重にコピーする→その場所
は親クラスとしてそのまま扱える (ただしポインタ
逆変換の問題がある)

13

インタフェース

9• インタフェースの継承においては、多重継承の問題は起きない (実装が存在しないから)。ただし継承す□ Smallatalk-80 など弱い型のオブジェクト指向言語でると矛盾が起きる場合には継承はできない。

は、継承→オブジェクトの互換性を意味していた□ 今後→実装の継承とインタフェースの継承はさらに分離• 実は Smalltalk-80 の時代から継承にはいく通りもされる方向?
の使われ型 (実装を借りて来る、概念的に共通、等) □ Java でインタフェースを使った簡単な例:
があることが分かっていた。

□ しかし、C++など強い型の言語オブジェクト指向言語が使われるようになると、「実装の継承」と「外から見た互換性」は一緒でなくてもよいことが知られるようになってきた• 「外からみた互換性」もまた、階層構造として取り扱いたい (そうでない言語… 例:

Emerald や GNU

G++の interface 機構) もあるが• 現在の潮流→実装の継承と界面 (インタフェース) の継承の分離• インタフェースの継承のことを subtyping と呼ぶことも

9.1型とシグニチャ□ シグニチャ(signature):

ある型が持っている (1) メ

ソッドの名前、および (2) 各メソッドの引数と返値の型の集まり• シグニチャに互換性 (conformance) があれば、型 Aの代りに型 B を利用可能• Emerald はこの互換性のみに基づく型検査を用いている• しかし一般的なオブジェクト指向言語では型の親子関係に基づいて互換性を定義 (Simula 以来の伝統だから? 「たまたま」互換性が生じるのを嫌うから?)9.2

インタフェース機能□ インタフェース:

シグニチャを定義するもの• インタフェース機能を持つメジャーな言語: Javaが代表的 (ただし通常の継承も持つ→やや中途半端/
保守的)□ インタフェースの継承もあり→インタフェースの階層構
造が定義できる→これに基づいて型の互換性が定まる

14

import java.applet.*;import java.awt.*;
interface Function {public double calculate(double x);}class Quadratic implements Function {double a, b, c;
public Quadratic(double a0, double b0, double c0) {a = a0; b = b0; c = c0;}
public double calculate(double x) {return a*x*x + b*x + c;}}
public class Sample10 extends Applet
implements Runnable {boolean running;double time;
public void start() {running = true; (new Thread(this)).start();}public void stop() { running = false; }public void run() {long basetime = System.currentTimeMillis();time = 0.0; repaint();
while(running) {try {Thread.sleep(100);} catch(Exception e) { }time = 0.001 *(System.currentTimeMillis() - basetime);
repaint();}}
public void paint(Graphics g) {g.setColor(Color.black);g.drawLine(0, 100, 200, 100);g.drawLine(100, 0, 100, 200);
g.setColor(Color.blue);drawFunc(g,new Quadratic(1.5*Math.sin(time), 0.7, -0.5));}
public void drawFunc(Graphics g, Function fn) {double x0 = -1.0;
double y0 = fn.calculate(x0);for(int i = 1; i <= 20; ++i) {double x = 0.1*i - 1.0;
double y = fn.calculate(x);g.drawLine(100+(int)(100*x0), 100-(int)(100*y0),100+(int)(100*x), 100-(int)(100*y));• それらすべてに「発明された理由」はもちろんある

x0 = x; y0 = y;}

• しかし「それらがすべてが今いるのか?」は別の問題}}□ 言語の多様な機能を使えば使うほど「難しく」もなる→「機能の増加」と「簡潔に/分かりやすく記述できる」の• さまざまな「関数クラス」はそれぞれ、実装の共通

トレードオフについて常に考える (例:部分はほとんどない→インタフェースのみ共通にすJava vs C++)るのが自然

• さまざまな「実行可能なクラス」も「実行する」メ
ソッド run() を持つ以外には共通部分はなくてよい

オブジェクト指向の利用技術

11→ Thread オブジェクト生成時に Runnable オブジェ □ オブジェクト指向言語→これまでの言語にないさまざま
クトを引数として渡すと、その run() をスレッドとな「道具だて」(例:

して実行させるようになっている

譲、インタフェース、…)

サブクラス、動的分配、継承、委• それをどのように使うか→なかなか難しい問題

Java の内部クラス

9.3□ どのような方向での利用があるか?

□ インタフェースを使ってアダプタクラスを作るような
場合:

11.1

• 小さいクラスが多数できてしまうので面倒• それらのクラスから元のクラスのメソッドを呼ぶの□ 美しいプログラム構造 (何が美しい???) ←オブジェク
ト指向言語の特性に合ったプログラム設計← OOSE(オが面倒

ブジェクト指向分析、オブジェクト指向設計、標準記法□ このため「クラス内部にクラス定義が書ける」ようになった--- UML ---):

public class X {...
return new Y();11.2

本講の範囲外再利用□ 再利用←書くよりは書かないで済ませた方が生産効率はよいに決まっている

class Y implements I {... ←この中で X のメソッドが呼べる}• なぜオブジェクト指向で再利用? → クラス、オブジェクトといった単位は従来の「サブルーチン」よ}り固まりが大きく、再利用に向いている□ さらに、いちいち「Y」のような名前を考えないで済ませることもできるように「無名の内部クラス」構文がある
public class X {...
return new I() {... ←先の Y の定義と同じもの}}• 再利用するものは何? コード?
まざまなレベルがある

11.3

設計知識?

→ さ

クラスライブラリ□ Smalltalk-80 →充実したクラスライブラリが付属→クラスライブラリを熟知すれば生産性が高まる、という• 記述が短くなるという点は便利だが、最初はちょっとブームに分かりづらい10

オブジェクト指向にあった設計• 実際にやってみると、よいクラスライブラリの開発/
クラスライブラリに熟知した人材の育成ともに簡単

オブジェクト指向言語の諸側面のまとめではない□ Smalltalk-80 クラスライブラリでは差分プログラミン□ 非常に多数の機能がある、ということは分かったと思う

15

グ (子クラスで親クラスの機能を少しずつ拡張していく)を多用→これもよい手法だと一時思われていた• しかしやってみると、よい差分プログラミングは難しい (クラス間の依存関係が大きくなりぐちゃぐちゃ

public void actionPerformed(ActionEvent e) {model.left(); repaint();}});
add(b2); b2.setFont(fn);b2.setLocation(120, 80); b2.setSize(60, 40);
b2.addActionListener(new ActionListener() {public void actionPerformed(ActionEvent e) {model.right(); repaint();}});になりやすい)□ 現在では、クラスライブラリはもちろん必要だが、整った機能を一式、分かりやすいインタフェースで提供するという当たり前の結論に

11.4

アプリケーションフレームワーク□ 抽象メソッド:}
public void paint(Graphics g) {lab.setText(model.getStr());}

親クラスで「自分自身へのメソッド呼び出し」を用いたメソッドを定義→子クラスでそれらの}メソッドを具体的なものに差し替え

interface Sample11Model {public String leftName();public String rightName();public void left();public void right();
public String getStr();}□ これをさらに発展させて、汎用的なアプリケーション全体の構造を予め定義しておく→その中のいくつかのクラスをサブクラス化してそこに各アプリケーション固有の部分を記述することでアプリケーションを完成させる• 例:

MFC、ET++、Choices、…

class Sample11App implements Sample11Model {String str = "STR";
public String leftName() { return "A"; }public String rightName() { return "B"; }public void left() { str += "A"; }public void right() { str += "B"; }public String getStr() { return str; }}• うまく当てはまれば生産性は高まるが、何をどうサ
ブクラス化するか、サブクラスはどのような規約に従う必要があるか、といったことを学ぶのが大変□ たとえば、アプレットもアプリケーションフレームワー
クの 1 つ• アプレットはクラス Applet のサブクラスとして作り、初期設定メソッド init() 描画メソッド paint()
等を必要に応じてオーバライドする• HTML 中にアプレットを埋め込む構文:

11.5

コンポーネント□ ここまでの再利用技術→基本的にクラス (群) が対象→

プログラマ向け (クラスベースとも言う)<APPLET CODD="Sample11.class" WIDTH=300 HEIGHT=200></APPLET>□ もっと簡単な「表示覧と 2 つのボタンがある」フレーム □ プログラミングをしない人に使える再利用技術→インス
タンスベースの再利用
ワークを作ってみました。

• インスタンスを生成し、そのプロパティ(属性、要す

import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;
public class Sample11 extends Applet {Font fn = new Font("Helvetica", Font.BOLD, 24);
Sample11Model model = new Sample11App();
Label lab = new Label(model.getStr());
Button b1 = new Button(model.leftName());
Button b2 = new Button(model.rightName());
public void init() {setLayout(null);add(lab); lab.setFont(fn);lab.setLocation(20, 20); lab.setSize(200, 40);lab.setBackground(Color.white);add(b1); b1.setFont(fn);b1.setLocation(20, 80); b1.setSize(60, 40);
b1.addActionListener(new ActionListener() {るにインスタンス変数の値) をカスタマイズする• カスタマイズしたインスタンス群をディスク等に保存しておき、それを取り出してそのまま動かす• そのようなインスタンスを「コンポーネント」と呼んでいる。ソフトウェア開発のためのコンポーネン
ト群→「コンポーネントウェア」、コンポーネント
ウェアに基づくソフトウェア開発→「部品組み立て
プログラミング」□ 代表的な成功例→ VisualBasic (部品:VBX、OCX…

COM、DCOM の部品)
• ほかにも国産の IntelligentPad、Java ベースのJavaBeans などいろいろある16• しかし、部品とその配線だけでできるプログラムで □ 例: AbstractFactory パターン
十分なの?
• Windows でも Mac でも X11/Unix でも同じに動作す
る GUI アプリケーションを開発するには???

デザインパターン11.6□ パターン:□ AbstractFactory パターンとは:「繰り返し現われるようなカタチ」□ (ソフトウェアにおける) デザインパターン:• Window、Button、Dialog などの汎用的なクラスを
用意する

オブジェ

クト指向ソフトウェア開発において、有効に使えるようなオブジェクト群の構成のパターン• そのサブクラスとして MacWindow、X11Window など
それぞれ用意する• 1990 ころから、Peter Cord 他がはじめた。日本では「ガンマ本」が有名になっている。ガンマ本はよ• WinFactory という抽象クラスを用意し、メソッドとして makeWindow、makeButton 等を用意する

く使うパターンを集めた「パターンカタログ」になっ• MacWinFactory、X11WinFactory などの具象クラス
でこれらをそれぞれ実装するている。

• なぜデザインパターン? → オブジェクトの接続関係のノウハウはかなり難しい (ちょっと思いつかない• アプリケーションの実行開始時に MacWinFactory などのインスタンスを作って WinFactory 型の変数にようなものもある) →それを蓄積しておいて流通さ格納し、以後それを利用する

せると、うまくはまったときに役立つ□ 例:

Command パターン

11.7

本節のまとめ• メニューの選択、画面上のボタンなどはどれも「何 □ オブジェクト指向にはさまざまな「道具」が含まれていらかの動作」を起動する→「動作をするオブジェク
る→その「道具」をどう使うか、についていろいろな工ト」を用意して、それをメニューやボタンに結びつ夫がある
けていけばよい。
□ 例:□ しかしこれらもまだほんの一部?

の「よりよい利用方法」が現われる (はず)Adapter パターン

• ボタンが押されたときに呼び出されるメソッドはある名前に決まっている。しかし実際に起きて欲しい□ それらの利用方法を個別に「新しい」と思って受け入れるだけでは、流行に追われるだけ→必要なこと:ことを実行するメソッドは別のメソッドである→「仲• その「新しい」技術がこれまで行われてきたさまざ
まなことの中にどのように位置づけられるのかを考介するオブジェクト」を用意して、それが橋渡しをすればよい。

える• 結局、ソフトウェアの生産において「自分が必要と□ 実は上記 2 つは前の例題に含まれていた。
□ 例:→これからより多くすることは何か」をまず考え、それに照らして必要

Visitor パターンなものを取捨選択する• オブジェクトの階層構造で構造化グラフィクスとか複合文書のようなものを作ったとする。「印刷する」「表示する」「ファイルに保存する」「スペルチェック

12

第 2 回課題する」等それぞれの場合について、各クラスにそれ用 □ 以下の課題から 1 つ以上 (4 を含む場合は 2 つ以上) をのメソッドを作るのは面倒である → どうする???
選択してプログラムを作成し、実験を行ってレポートを提出せよ。考察まできちんと書くこと。

□ Visitor パターンとは:• 各オブジェクト側には「accept」というメソッドを□ (1) Java の例外機構の処理速度を計測せよ。通常のメ

1 つだけ用意しておく。その引数として、「印刷用の Visitor」「表示用の Vsitor」などさまざまなソッド呼び出しについても計測し、比較すること。同じ場所で処理する例外の個数、try ...

catch の入れ子

数、手続きを戻る個数による影響も考慮すること。それ

Viritor オブジェクトをそのつど渡せばよいに基づき、どのような場合に例外処理が適し、どのよう17な場合には適さないかを考察すること。自分が使用した

Java 実行系の例外処理方式が推察できるとなおよい。
□ (2) 自己反映機能の例題を改良して、「オブジェクト電卓」にせよ。つまり生成したオブジェクトをいくつか「電卓のメモリ」に入れておいて、あるオブジェクトのメソッドを呼ぶのにメモリに入っているオブジェクトを引数として渡せるようにする。これにより、任意の Java オブジェクトをプログラムを書かずにいじって見ることができる。
□ (3) 「簡単なアプリケーションフレームワークの例題」に入れる別のモデル (アプリケーション) を作って差し替えてみよ。また、もうすこし役に立つことができるよ
うにこのアプリケーションフレームワークを改良してみよ。
□ (4) その他、Java を用いて「オブジェクト指向言語らしい」プログラム (アプレットでもアプリケーションでもよい) を作成せよ。

参考文献[1] リメイ, パーキンス著, 武舎ほか訳, Java 言語入門, プレンティスホール出版, 1995.
[2] モリソン, エイブラン著, 福井ほか訳, 続・ Java 言語入門, プレンティスホール出版, 1998.

[3] Goldberg, Robson, Smalltalk-80 --- The
Language, Addison-Wesley, 1989.
[4] ガンマ他著, 本位田・吉田監訳, オブジェクト指向
における再利用のためのデザインパターン, ソフト
バンク, 1995.

18