• 検索結果がありません。

DVIOUT-oolin

N/A
N/A
Protected

Academic year: 2021

シェア "DVIOUT-oolin"

Copied!
39
0
0

読み込み中.... (全文を見る)

全文

(1)

プログラム理論と言語 Part2-1-1

オブジェクト指向

操作対象の理解と言語の効率性

関連したデータの塊を1つの「もの」として認識

(1)

クラス=「もの」と「もの」に付随した操作系

(2)

クラスのことはクラス内部で処理

ブラックボックス化し、外部仕様を明確化

⇒ 部品としての再利用、安全性

(3)

「もの」と処理の独立性を高め処理も部品化

操作そのものでなく、操作の型情報のみで処理を記述

⇒ コードの抽象化、再利用

⇒ 役割・機能と「もの」を分離し独立性を高める

(4)

複雑さに対処するために階層として体系化

⇒ 修正・追加・利用(特にライブラリ、ユーティリティ)

人の対象理解・言語的表現と合致する

特に個別事例に依存しない抽象表現 ⇒ 言語の効率性

(2)

JAVA

はオブジェクト指向言語か?

その気になれば、オブジェクト指向でプログラムが書ける

一般に長くなるが、

可読性、安全性、再利用性は高まる

そうでないプログラムも書ける

「体系的でない」ものも多い

簡単な場合はそれでもすむが、複雑になると、

修正・拡張・再利用が困難

プログラムを書くという観点からは、

オブジェクト指向は処理対象と処理を体系化す

るための作法であり、オブジェクト指向言語は

そのための言語的装置

(*)

(*) 本講義において Java を題材にする理由: 1. オブジェクト指向の主要な概念は全て Java で説明できること 2. プログラム言語において重要な型概念を十分に講述できること 3. 様々な場所で実際に良く使われている言語であること 4. CS実験等をとおして、C言語の理解が進みつつあることを考 慮し、Cと構文的に近いオブジェクト指向言語であること ポインタについて: Java: 実際は「ポインタのお化け状態」 ポインタを駆使するプログラミングは一般に難しい Java: 比較的に安全にかつ容易に実現 本講義: 上記の事情をある程度説明する

(3)

はじめにオブジェクトありき

まず処理において如何なるオブジェクトが必要かを考える

対象の分析・理解

⇒ その理解を言語的に顕在化させるプロセス

⇒ 「もの」の定義書としてのクラス

「もの」に付随したメソッド(演算、基本操作)群

⇒ メソッドを持つ「もの」として体系的に扱う

その後に、メソッドにより「もの」を操作・処理する手続き

オブジェクト指向言語=オブジェクト定義・操作言語

では、「もの」とは何か?

哲学論議はここではしない

計算機の中での出来事

⇒ メモリ上の構造物として表現・翻訳される「もの」

(4)

配列:プログラミングにおいて不可欠な「もの」

これも立派なオブジェクト

int[] a; a = new int[3];

データの集まり

{a[0], a[1], a[2]}

を1つの配列と

して 纏めて扱う。

new int[3]

: 配列領域を割当・生成

配列は要素から構成され、

添字を与え要素へアクセス

特に

a

は領域全体の名前(参照)

領域を生成する仕掛け

一般のオブジェクト:

フィールド名を持つ要素からなり

添字の代わりにフィールド名(変数)で要素にアクセス

特定のデータ領域を占有し、

領域全体はオブジェクト参照変数で参照

領域生成は「コンストラクタ」で行う

(5)

3角形は3つの点からなる: まず「点」を定義

定義は対応するクラスの記述で与えられる

class Point {

//

フィールド変数

double x, y, weight;

//

コンストラクタ(生成方法)

Point(double x, double y,

double w) {

this.x=x; this.y=y;

weight=w;

}

//

インスタンスメソッド

//

各オブジェクトに直接作用

void show() {

System.out.println(

"x="+x+", y="+y+

" with weight "+weight);

}

}

フィールドアクセス:

this

からは、フィールド名

x

一般に、

a.x

Point a

が参照している

オブジェクトの

x

フィールド

識別子が特定する位置の実体に対し

x

のフィールド値をとりだす

Note: オブジェクトはCの構造体、参照変数は構造体型のポインタ変数 に対応する。メソッド(関数)と一体化して使う点がオブジェクト 指向を表しているが、上記の図ではその事情を十分説明できていな い(インスタンスの図)。後で詳しく述べる。

(6)

Cの構造体

オブジェクトと参照変数はCの構造体とそのポインタと似ている

似て(メモリ上の構造物という点は同じ)

非なるもの (使い方、その作法、安全性に対する配慮が全く異なる)

struct student{

int stnum;

char stname[16];

};

...

struct student *stpt;

/*

ポインタ

*/

...

stpt->stnum

(*stpt).stnum

class Student{

int stnum;

String stname;

}

...

Student st;

//

参照変数

...

st.stnum

stpt, st

が参照する実体の

stnum

フィールドをゲット

stpt, *stpt

を区別

参照変数は

Student

実体、と考えてよい書き方

「非なる点」としては、そもそもオブジェクトがメモリの特定の領域 を「占有しているか?」の問題もある。 Java: 占有状態(システムが管理) C: アドレス計算が可能、型チェックが甘い等により上書きの危険性 ⇒ プログラマの責任

(7)

参照変数はポインタか? - Cとの比較

答え: 広義の意味のポインタか? ⇒

Yes

Cと同じポインタか? ⇒アドレス計算を禁止、参照機能に限定

識別子の詳細は「知る必要はない、知ったとしてもその事実をコードに 反映できない」。識別しアクセスできることが論理レベルで保障さ れていれば十分。以下、実体を特定できる論理的なものとして識別 子を考える

(8)

参照関係と内部状態の変化

厳密表現 :

Point

の参照変数aが参照する(指す)オブジェクト

略した表現:

Point

a (単に、「点」a)

「仮の名前」

: 変数と実体の参照関係は動的に変化。内部状態も!

計算過程: 新規オブジェクトを生成しながら、

(1)

参照関係の変化、

(2)

オブジェクトの内部状態の更新

上記はメソッド実行時

(9)

メソッド呼出とパラメータ

実体内部状態: メソッドにより更新され変化

実体の識別子: 何ら変わることはない(永続性

*

⇒ 識別子(名前)と実体は同一視

基本データ型の引数: 値を引数パラメータに代入

(

値呼出

)

(*) 自オブジェクトを示す参照変数 this への代入(代入式の左辺)は禁 止で、コンパイルエラー

(10)

つぎに、3角形を定義する

class Triangle {

//

スタティック変数

static final int nofPoints=3;

//

オブジェクトはフィールドで規定

Point[] points

= new Point[nofPoints];

//

インスタンスメソッド

//

オブジェクトに作用する関数

void show() {

for (short i=0;i<nofPoints;i++)

points[i].show();

}

}//

3角形に対する

show()

点に対する

show()

を使って定義

クラス情報

(

)

で識別

static final int nofPoints=3;

クラスに固有(インスタンスに依存しない)変数で クラスのスタティックメンバーともいう。 final 指定変数は、上書き不可。ただし、下記の注意が必要となる: 1. 基本データ型: 文字通り、「定数」 2. オブジェクト: 参照先オブジェクトは変わらない。しかし、そ の内部状態は変更される可能性 「定数」にするにはカプセル化が必要

(11)

コンストラクタ(オブジェクトの占有領域を作成)

デフォルト生成:

Triangle t = new Triangle();

引数なしの特別な手続き

Point[] points

= new Point[nofPoints];

に従い実体を生成し t にセット

この場合、配列領域の確保(割当)

明示的な生成=デフォルト+α

Triangle t =

new Triangle(p0,p1,p2);

右辺実体

(

oid)

t

にセット

Triangle( //

引数付の手続き

Point p0, Point p1, Point p2) {

points[0]=p0; points[1]=p1;

points[2]=p2;

}

クラスのインスタンス化: コンストラクタを適用し、実体オブジェクトを生成する行為 デフォルトコンストラクタ:引数なしで Triangle() {} クラス定義におけるデフォルトコンスタラクタの扱い 明示的コンストラクタを使用しない⇒ 宣言せずに使える 明示的コンスタラクタを使用 ⇒ デフォルト Triangle() を使うときはそれも明示する

(12)

定義・生成・操作

オブジェクトの定義をクラス記述で与え、

コンストラクタでオブジェクトを生成し、

インスタンスメソッドでオブジェクトを操作する

class triangletest {

public static void main(String args[]) {

Triangle t

= new Triangle(

new Point(1,2,3), //

各点実体(の識別子)

new Point(4,5,6),

new Point(7,8,9)

); //

右辺のオブジェクト

(

の識別子

)

//

左辺の変数(に代入)に参照させる

t.show(); //

3角形

t

show()

を適用

}

//

オブジェクトの操作は手続きで記述

}

//

「手続き的なオブジェクト定義・操作言語」

「論理型のオブジェクト指向言語 (OOLP)」、 「関数型のオブジェクト指向言語 (Common LISP)」 この他、「オブジェクト指向のデータベース」、 「オブジェクト指向の COBOL!」などもある。 扱うデータの基本単位としてオブジェクトを許すと、こうなる

(13)

様々なメソッドの定義と組合せ:手続きを記述

class クラス 名{ //インスタンスメソッド:「もの」に対する 基本操作、または、複合操作 出力型 (クラス名または基本データ型) // (基本操作を組合せた操作) メソッド名 (パラメータ型 変数名, ...) { メソッド本体(そのクラスのオブジェクト に対し行う処理 or オブジェクトがそのメソッドで振る舞う動作の記述) class Point { double x, y, weight; double length(Point p) {// 2点の距離 return Math.sqrt(Math.pow(x-p.x,2)+Math.pow(y-p.y,2)); }// sqrt: 平方根、pow: べき乗。Math クラスで定義 ... } class Triangle {

static final int nofPoints = 3; // 大域変数(定数) Point[] points = new Point[nofPoints];

//

double sumOfLength() {// 3つの辺長の総和 double sumOfLength = 0.0;

for (int i=0;i<nofPoints;i++) // i++ は i=i+1 の略記

sumOfLength += points[i].length(points[(i+1)%nofPoints]); return sumOfLength;

} // 整数 n, m に対し、n % m は m を法とした剰余 public static void main(String args[]) {

Triangle t = new Triangle(

new Point(1,2,3), new Point(4,5,6), new Point(7,8,9) ); if (t.checkTriangle()) System.out.println("辺の長さの総和 = "+ t.sumOfLength()); else { System.out.println("3角形ではありません。要データチェック"); t.show(); }; } }

(14)

用語の整理1: インスタンスとオブジェクト

オブジェクト: 操作の「対象」で、占有領域

(

インスタンス

)

それを処理するメソッドを持つ

Cでも構造体のインスタンス化は行う。しかし、そのインスタンスは関 数(メソッド)と言語的に一体化しているわけではない。プログラ マが必要に応じて、そのインスタンスへのポインタを関数に渡して 適用・操作するだけの話 一方、Java では、最初からインスタンスはメソッドと一体化されて扱わ れることが大前提であり、そのことがクラス定義で明示される。そ れは体系的に操作対象を扱うための流儀であると理解すること。

(15)

操作により「もの」をクラス分けする

オブジェクトがどのようなメソッドを使用できるかはクラス

定義で決まる。逆に言えば、各オブジェクト毎にメソッ

ドを定義し保持するのは不経済

オブジェクト毎に異なるメソッドが必要な場合:

別クラスの「もの」 ⇒ メソッドがクラスを特徴づける

上図における「人」は識別子と考えてもよい。以後、概念図において「人」 記号は、識別子もしくはインスタンス、とする

(16)

カプセル化

クラスの利用、クラス間相互作用は一般に複雑化

外部仕様: クラスとは? ⇔ ○○のメソッドを持つ「もの」

~ メソッド中心の設計

仕様と実装の分離独立化

仕様に関わるメソッドのみ公開

外部からは公開メソッドのみで操作

内部的な実装は

private

隠蔽し独立に管理

オブジェクト指向はさらなる構造化を促す: 独立したクラスの列挙から、構造化されたクラス群 1. クラス階層、インタフェース階層 2. 内部クラス(一般には クラスの nesting)

(17)

アクセス制御の原則

フィールドは原則

private

(不用意なアクセスによる状態参照・更新を抑制する)

ゲッター

getAttr()

、+セッター

setAttr(...)

メソッドに対するアクセス制御

同一パッケージ(同一ディレクトリ)を想定した場合:

無修飾のメソッド: パッケージ内で公開

private

メソッド: クラス内のみで有効

一般には、パッケージの作り方に依存

(

特に、

public)

複数のパッケージ(複数のディレクトリ)の場合:

public: 全てに公開。protected, final: クラス階層導入時

むやみに意味もなく public をつけない! パッケージ配布・公開の際の外部仕様 ソースコード解読時の構文上の情報 インタフェースの定数とメソッドは must be public protected: パッケージ内+下位クラスに公開 final 定数・メソッド:上書きを許さない

クラスに対しても、public, private, final、無修飾等の修飾が可能. 例えば、public にしないと他パッケージから利用できない final にすると、継承・拡大を許可しない

(18)
(19)

リスト:再帰とカプセル化の例

再帰的構造は「型」で表現

(インタフェース、後述)

リスト(要素の並び)

x

1

, x

2

..., x

n

boolean empty();

int head();

List tail(); List cons(int x);

フィールドは上記を満たすべく設計

実現手法に依存(配列、ポインタ、

...

class List {

int head() {return element;}

private int element;

List tail() {return tail;}

boolean empty() {return tail == null;}

private List tail;//

生成時の初期値は

null

List cons(int x) {// this

は自オブジェクト(の識別子)

List list = new List(); list.element = x; list.tail = this;

return list;

(20)

// list

の付加的なメソッド定義

int sum() {//

再帰の構造はデータ構造と一致

if (empty()) return 0;

else return head() + tail().sum();

}

// Note:

停止性・正当性は長さに関する数学的帰納法

void show() {

if (tail == null) System.out.println();

else {

System.out.print(element+", ");

tail.show();

};//

ここでは

show()

sum()

List

で定義

}//List

の応用として別クラスのメソッド

(static or not)

でも良い

}

class test {

public static void main(String args[]) {

List list = new List();//

デフォルトコンストラクタ

for (short i=0;i<args.length;i++)

list = list.cons(Integer.parseInt(args[i]));

list.show();

System.out.println(" sum=" + list.sum());

}

(21)

ネータ帰納法

クラス

C

の実体の集合を単に

C

と書く

(C,

¹)

が順序集合

(poset)

とする

(順序の決め方は一意ではないが、実体間の部分構造関

係があれば、自然に順序がはいる)

(C,

¹) :well-founded ⇔

def

無限下降列

...

≺ x

i−1

≺ x

i

≺ ... ≺ x = x

0

が存在しない

リストの場合:

[element == any, tail == null]

が極小元

int element = 0;

と初期化しておけば

[element == 0, tail == null]

が最小元

ネータ帰納法:

P (x)

: 証明したいこと

∀ x ∈ C (∀ y ∈ C(y ¹ x

P (y))

P (x))

∀ x ∈ C P (x)

線形リストは、特殊ケースである数学的帰納法

部分構造関係 ~ 要素数の大小関係

(22)

構造帰納法

∀ x ∈ C (∀ y ∈ C(y ¹ x

P (y))

P (x))

∀ x ∈ C P (x)

ネータ帰納法が必要な場合例: 木に対するメソッド

class BinaryTreeNode {

private Content content;//

ノードが保持する情報

//

別クラスで定義

private BinaryTreeNode leftChild, rightChild;

boolean leaf() {

return leftChild==null && rightChild==null;

}

...//

部分木 ≦ 木 の順序。

(23)

クラス間相互作用の例

class Polygon {

private Point stdP;

private Point[] points;

Point getStdP() {

return stdP;

}//

アクセスメソッド

...

}

class Point {

private double x, y;

private Polygon cp;//

帰属多角形

boolean steeper(Point ap) {

return cp.getStdP().slope(ap) < cp.getStdP().slope(this);

}

double slope(Point ap) { ... }

...

}

図における人は実体オブジェクトで、矢印は、実体間の参照(フィール ドの実現値)関係を表す 識別子間の参照関係が形成される 1. メモリ上の現象としては、ポインタのお化け 2. 理論上は、複雑なグラフ 次ページの部分クラスは、参照関係の複雑さを軽減させる効果がある

(24)

内部クラス:複雑さを軽減させるための構造化

class Polygon {//

内部クラス

Point

の外部クラス

private Point stdP; private Point[] points;

...

class Point {//

多角形の内部クラス。点実体は多角形実体の属性にアクセス可

private double x,y; //

点の実体化は多角形の実体

cp

の中で行う

(*)

boolean steeper(Point ap) {

return stdP.slope(ap) < stdP.slope(this);

}...

}//

カプセル化を壊さずに、記述が簡潔化

}//

クラス間の編成上・組織上の構造を明示。後述のクラス階層とは別

(*)「実体の中に実体を作る」~ 占有領域の中でインスタンス化すると理解 Polygon のコンストラクタで多角形をインスタンス化する際、もしくは Polygon のインスタンスメソッド中で点をインスタンス化 A外・B内、B外・C内、のような外部・内部が層化される場合、外側の実体から 順次生成する(Aの実体からCのコンストラクタは直接呼び出せない) 実体化に関する制限と、メンバアクセスの話は別。メンバーを隠蔽するには、きち

(25)

用語の整理2:

obj.m(p1,.., pn)

の読み方

『オブジェクト

obj

に対し、

メソッド

m(p1,.., pn)

を 適用』

obj

に対し、

m(p1,.., pn)

を 実行』

『オブジェクト

obj

に対し、

メソッド

m(p1,..,pn)

を 呼び出す』

メソッドはオブジェクトの所有物

本講義では、特に区別しない

この他、オブジェクトをエージェント(処理主体)と考え、

『オブジェクトにメッセージを 送る』

(26)

メッセージパッシング

『オブジェクト

obj

(レシーバ)に、

メッセージ

m

をパラメータ

p1,.., pn

で送る』

送られた方は

m

を処理するメソッドを持つ

メッセージ ~ メソッド

class Teacher {

...

void assignRept(Student st,

Task task) {

Answer ans = st.report(task);

// st

に「

report

せよ」との

//

メッセージを発信し、

//

結果を

ans

で受ける

if (ok(ans)) {...}

else {...};

...

}

class Student {

//

メッセージを受けたときの

//

動作 ~ メソッドそのもの

Answer report(Task task) {

...

Answer myAnswer

= new Answer(...);

return myAnswer;

//

解答を

myAnswer

で返す

}

}

実際の例題は、FAのシミュレーション (statisfa.java, fa.java) で示す

(27)

メッセージパッシング例

各状態をオブジェクトに

状態は、受理

/

非受理の情報を持つ

入力記号列

str

を受け取り、次の状態

に処理依頼のメッセージを投げ、以降

の処理を委ねる

class State {

private static final short nofSymbols = 2; //入力記号は {0,1} private String name;

private boolean accept; // 受理状態情報

private State(String n, boolean f) {name = n; accept = f;} private State[] nextStates = new State[nofSymbols];

void defineNextState(State next0, State next1) { nextStates[0] = next0; nextStates[1] = next1; }

private short head(String str) {return Short.parseShort(str.substring(0,1));} private String tail(String str) {return str.substring(1,str.length());}

private void process(String input) { if (input.length()!=0)

nextStates[head(input)].process(tail(input)); else

if (accept) System.out.println("OK"); else System.out.println("NG"); }

public static void main(String args[]) {

State q0 = new State("q0", false); State q1 = new State("q1", false); State q2 = new State("q2", true); State q3 = new State("q3", false); q0.defineNextState(q1, q3); q1.defineNextState(q3, q2);

q2.defineNextState(q1, q3); q3.defineNextState(q3, q3); q0.process(args[0]);

} }

(28)

Definition of

A: A

consists of

1,

2,...

オブジェクト指向: 言語的な定義のスタイルに対応

Aの定義書: 『クラスAのオブジェクトは

クラス(

or

基本型)B

j

のフィールドからなり

○○のメソッドを使って操作します』

(構成要素(

part of, has_a

関係)

クラスのメンバー

isa

, or

is defined as a

such that ...

extends

...

「AはBを継承し拡大」

Aに固有なフィールドやメソッドを追加

has a role/function of

B: インタフェース

implements

機能を持つ ~ 役割・機能を実現する手段・メソッドを

具体的に持つ(実装)

⇒ 多態性(ポリモルフィズム)

⇒ 様々な役割や機能を実装クラスで記述

(クラスと機能の分離・独立化を促進)

(29)

クラス定義のまとめ

class Point {

private double x,y;

Point(double x, double y) { this.x = x; this.y =y; } void show() { System.out.println(" x="+x +", y="+y); } } class Triangle { private static final

int nofPoints=3; private Point[] points

= new Point[nofPoints]; private Triangle(Point p0, Point p1, Point p2) { points[0]=p0; points[1]=p1; points[2]=p2; }

private void show() {

for (short i=0;i<nofPoints;i++) points[i].show(); }

public static

void main(String args[]) { Triangle t = new Triangle( new Point(1,2), new Point(4,5), new Point(7,8) ); t.show(); } //void main(String[]) は } //ここでは Triangle に組み込んだ 実行時: クラスは 生成されたオブジェクトの集まり + 大域変数(static variable)の値 を管理 コンパイル時: オブジェクトの定義書として記述 の集まり。記述は、クラスのメンバー(構成 要素)と呼ばれる変数やメソッドの各々に対 し行われる フィールド変数: オブジェクトの属性 基本データ型もしくはオブジェクト参照変数 フィールド記述がコンスラクタの デフォルト動作を決める インスタンスメソッド: オブジェクトに直接作用する手続き オブジェクト: this で表記(必要ならば) スタティック (static) メンバー スタティック変数:クラスが保持し、 各オブジェクトに依存しない変数 スタティックメソッド: オブジェクトに依存しない手続き public static void main(String args[])

java クラス名 引数 そのクラスの main が実行される。 クラス毎につけてよい 型は void main(String[])。引数型を別に すると「オーバーロード」 左記では main は Triangle のメンバー 別クラスで main を動作させる場合

Triangle(...) と void show() の private 指定 は外す(メソッドにアクセスできないから)

(30)

スタティックメンバーについて

オブジェクト指向では、先んずオブジェクトとクラス設計

その次に手続き等の設計

個々のオブジェクトに依存しない定数・変数・メソッドがあ

れば、スタティックメンバーにする

具体にスタティックメンバーが必要となる場面例:

コンストラクタに準じた作用を持つメソッド

クラス固有の定数やオブジェクト(

static final

個々の「もの」に依存しないクラス固有の処理手続き

基本データ型に関する型変換や数値関数など

「ラップクラス」や

Math

で提供される

int Integer.parseInt(String),

double Math.sqrt(double)

(31)

再帰呼び出しに関する補足

C

同様、

Java

でも再帰は(

static, instance methods

ともに)

使える。関数型でやったようなスタイルでのメソッド定

義もOK。

処理系からみると、再帰と非再帰の区別は全くない!

一般形

f

の定義本体中で別のメソッド

g

を呼び出す

再帰

特に

g = f

なだけで何ら処理は変わらない

ただし、計算量に注意すること

// フィボナッチの数列 f(n)=f(n-1)+f(n-2), f(1)=f(0)=1 class Fibonacci{

static int rec_fib(int n) {//この場合、指数爆発 if (n==0 || n==1) return 1;

return rec_fib(n-1)+rec_fib(n-2); }

static int it_fib(int n) {//繰り返し

//定数領域・線形時間 if (n==0 || n==1) return 1;

int nMinus1 = 1, nMinus2 = 1, tmp; for (int i=1;i<n;i++) {

tmp = nMinus1; nMinus1 = nMinus1 + nMinus2; nMinus2 = tmp; };

return nMinus1; }

(32)

スパゲッティプログラム:よくあるパターンその1

static void main(String args[])

で全てを書こうとし

補足的に

static

メソッドを用いる

⇒ スパゲッティプログラムへの道

処理対象と手続きの両方が構造化されていないため、

⇒ バグとり・修正・再利用・機能の追加が困難、

書き直した方が早い

修正箇所、機能追加、デバッグ箇所を

特定しずらく、ズルズルと全体がついてくる

⇒ スパゲッティ (結局、多くの箇所を「食べないとダメ」、

もしくは、切るのが大変)

(33)

スパゲッティプログラム:よくあるパターンその2

Cで書いていたとき、構造体とポインタに苦手意識があり、

その惰性でつい配列だけで書いてしまう。

関連する配列や変数がばらばらで、

関連性を見出すためには「スパゲッティ状態」

コメントをつけて対処する人も多い

⇒ コメントはダラダラとつければ良いというものでない

OOPL

構造体と参照は簡単にかつより安全にカプセル化できる:

⇒ クラスを定義し、メソッドで実体を処理する

(34)

スパゲッティプログラム3:例

static int nofItems = 0; // 商品数 static int[] stock = new int[10000],

price = new int[10000]; static String[]

itemName = new String[10000]; ...

static void addItem(int initialStock, int price,

String itemName) { if (nofItems == 10000) {

System.out.println("sorry, full list"); return; }; stock[nofItems] = initialStock; price[nofItems] = price; itemName[nofItem++] = itemName; } まず、重要な定数 10000 のスパゲティ 後で商品コードも必要になった ⇒

追加: static int[] code = int[10000]; 関連するメソッドの引数や本体部の修正 static void addItem(

int initialStock, int price, String itemName, int codeNumber) { ... code[nofItems] = codeNumber; さらに、商品コードで商品リストをソート code の値による並び替えで、同時に、stock, price, itemName 配列も操作 修正箇所が増え、しかもその場所が集中して いるとは限らない 喩え話: 中に色々なものが入っている箱を、他に移動するとき、ある順序で 並び替えるときに、箱の中身をバラバラにして移動、あるいは並べ 替えますか? この場合の箱 ~ オブジェクト 箱の中身 ~ フィールド クラスによるカプセル化

(35)

OOPLでは

class Item {

private int stock, price;

Item(int initialStock, int price) { stock=initialStock; this.price=price; }

... その他のメソッド ... }

class ItemList {

private static int MaxNof = 10000; private int nof = 0;

private Item[]

list = new Item[MaxNof]; void add(Item item) {

if (nof != MaxNof) list[nof++] = item; else System.out.println("full list"); } } code や name の追加は対応する フィールドとコンストラク タの修正のみ add(Item) は修正の必要なし 一般に、参照変数で実体を丸ごと 渡すので、関数・メソッドの 引数は少なくなる また、例えば、ソートのときも、 item.code の大小関係で、実 体そのものを並び替えれば それでOK 例えば実体の交換: Item tmp = itemList[i]; itemList[i] = itemList[j]; itemList[j] = tmp; 使い方は、商品名フィールドが追加されたとして、例えば ItemList itemList = new ItemList();

itemList.add(new Item(100,1200,"製図用シャープペン"));

集合やリストは、java.util に色々定義されている。自分で適宜定義し ても良い。インタフェースで話す「キャスト」を使う

class MyList {

private static int MaxNof = 10000; private int nof = 0;

private Object[] list = new Object[MaxNof]; void add(Object obj) {

if (nof != MaxNof) list[nof++] = obj;

else System.out.println("sorry, full list"); }

(36)

オブジェクト指向のストーリー

最初から、必要なオブジェクトとクラスを切り出す作業

⇒ 他のクラスとの切り分け・分節化 ⇒ 部品化

先んず対象のモデリング・設計、

その次にアルゴリズムの設計と解析

その次の次にプログラミング技法

優れたモデル ⇒ 優れたパッケージ(クラスの集合)

⇒ 保守・拡張・利用が楽

(37)

本日の演習問題1

整数を要素とする線形リスト(要素の並びを表し、参照構造

が一方向のもの)について述べた。一方、CS実験で、

双方向リストをCのポインタで実現する課題を行ったと

思う。課題の手引書と本講義の線形リスト

Java

プログ

ラムを参考にし、実験課題に対する

Java

のクラスを定

めよ

(38)

本日の演習問題2

下記の2つのメソッドを当該クラスに追加し なさい。ただし、(平面上の)点とベクトルを 区別するために、ベクトルのクラス MyVec-tor を定義し、それを利用した上で下記のメ ソッドを当該クラスに追加すること。 1. Point クラスのメソッド:

boolean checkPointOnLine(Point p1, Point p2);

パラメータで与えた2点と自オブジェクトが一直線上にあれば true, otherwise false.

2. Triangle クラスのメソッド: double computeArea(); ベクトルの内積を用いて面積を計算し出力。 演習問題解答: その詳細レベル 仕様をかくつもりで 1. クラス名、クラスの各メンバーの仕様 2. 各メンバーの仕様: (a) 変数であれば、その型(クラスもしくは基本データ型)、お よびその役割・機能の説明をつける (b) メソッドであれば、出力およびパラメータ型を明記した上で、 その動作について正確な日本語で記述すること。ただし、 (1) メソッド中で、(別のオブジェクトに対し)メソッド呼 び出しを行う場合はそのことも明記 (2) スタティックの場合は、static を明示 Java 言語の構文で書けるなら、なお良い

(39)

本日の演習問題3:有限オートマトン

右図は受理機械としての有限オートマトンF Aの例である。ただし、q0 を初期状態、q2 を受理状態とする。受理する言語は {(01)n | n ≥ 1} 1. FAには{0, 1} の入力記号系列が与えられる 2. 各状態で、入力系列 σ0σ1...σn−1 (n は、入力系列長)を受け取る。 挙動は: (1) 先頭記号 σ0 により次の状態 qnextを決め、 (2) qnextに残りの入力系列 σ1...σn−1 を渡し以後の処理を委ねる 3. 入力系列が空列(記号が残っていない、n = 0)のときは、最終状態 なら「受理」、それ以外の場合は「非受理」のメッセージを出力し、 全体の処理が終了 課題: FAの動作を模倣するプログラム(クラス State)を書くとして、 必要なフィールドとメソッドを与え、その型ならびにその挙動を説 明せよ。メソッド中で、別のオブジェクトに対するメソッド呼び出 しを行う場合はそのことも明記すること。 ヒント: 各状態をエージェントと考える。ただし、依頼先 (次の状態) か ら結果をもらう必要はこの場合ない。完全に処理を委ねる 有限オートマトンは、演習でやったと思うが、自動販売機の状態遷移 モデル、文字列検索における正規表現など、広く使われている

参照

関連したドキュメント

「文字詞」の定義というわけにはゆかないとこ ろがあるわけである。いま,仮りに上記の如く

④改善するならどんな点か,について自由記述とし

そればかりか,チューリング機械の能力を超える現実的な計算の仕組は,今日に至るま

スライド5頁では

3.排出水に対する規制

[r]

 事業アプローチは,貸借対照表の借方に着目し,投下資本とは総資産額

検討対象は、 RCCV とする。比較する応答結果については、応力に与える影響を概略的 に評価するために適していると考えられる変位とする。