プログラミング言語II page2(update:2009/11/10)
目次
[目次]
構造化分析や設計で大きなプログラムを作成可能
人間が行ってきた仕事を計算機を利用して効率的に行いたいとします。この場合に人間がやってきたことをよく見直して整理するこ とから始めるのが適当でしょう。仕事上の文書の書式と処理手順を調べて、書式をデータ構造で表現すれば文書はその型の変数となります、処理手順はこの変数を処理する関数となるでしょう。 データフロー図(DFD)やデータ辞書を作成して仕事を階層的に詳細化し、個々の処理を関数として実装することで大きなプログラムも作ることが出来ます。C言語はこのような構造化分析・設計の結果をプログラムするのに適した言語になっています。
しかし、再利用や更新が容易ではない
しかし、より複雑なプログラムが要求されるようになると、全てを1から作っていては無駄が多いのでプログラムの再利用性が求められるようになります。あるいは、プログラムが長期間使われているうちに様々な更新が必要になり、プログラムの更新が容易であることも求められています。
このような要求を満たすためにはプログラムを交換や更新が容易な部品で構成する必要があります。 C言語のプログラムでは関数が部品に相当します。例えば文字を出力するprintf関数の様にライブラリーの関数を自分のプログラムで 部品として使えます。更新の場合は元のソースプログラムがあることが前提ですが、プログラムをコピーして変更することも可能です。
しかし、データ構造については更新が容易とはいえません。例えば、新しいデータを扱うためにデータ構造に何かを追加する場合を考えてみてください。更新されたデータ構造を使っている全ての関数を見つけて変更することが必要になります。何かを変えたときに、その影響が及ぶ範囲を狭くすることが更新の手間を減らす上で重要です。しかし、データと処理を個々に構造化したプログラムではデータ構造の変更が広範囲に影響を与えるのを防ぐことができません。
さらに、複数の部品で構成される関数ライブラリー、例えばGUIの関数ライブラリーなどは、その中の1つの関数だけでは目的を達成することが難しく、ライブラリーの関数の役割を理解して、適切な順番で複数の関数を呼び出す必要があります。このような場合、再利用しようとすると関数の役割や仕組みを学習し順番を守って関数を呼び出さなければなりません。再利用する側の負担は非常に大きいものとなります。
抽象データ型を使うとデータ型更新の影響を限定できる
データ構造の問題に対応するための、抽象データ型と呼ばれる手法があります。抽象データ型はデータ構造とそのデータを読み書きする関数を合わせて1個の部品とします。データ構造は部品外の関数には見えないようにして、外部からは部品内の関数経由でのみデータにアクセスできるように制限します。 このようにするとデータ構造を更新しても直接的な影響は部品内の関数に限定されます。
データ構造を変更したり新しい要素を追加したりしても、外部から従来と同じ名前の関数を呼び出して同じデータが参照できれば、変更は外部に波及しません。
プログラムのモジュール化
抽象データ型ではデータ構造と関連する関数がまとまって1個の部品となります。C言語などではソースファイルを1つのモジュールとして、ある程度これを実現することが可能です。しかし、部品として組み合わせることが可能な標準化された構造を強制できるわけではありません。
モジュール:まとまった機能を持つ部品で交換や置き換えが可能なもの。
例えばパソコンに入れるメモリーモジュールやグラフィックスボードなど。
プログラムをモジュール化するとき、どのようにモジュールに分けるかが問題になります。人間が行ってきた仕事を書類とその処理でモデル化するのではなく、仕事上の役割を単位に分解します。役割に伴う責務は決めるが内部の書類の形式や処理は自由 とします。責務の部分は外部からの要求に答える公開された関数とし、データ構造や内部処理の関数は非公開とします。
オブジェクトの構造
上記の役割に伴う変数や関数をまとめた単位 をオブジェクト(object) と呼びます。 オブジェクト内のこれら変数や関数をまとめてメンバー(member)、中身の変数をフィールド(field)、関数をメ ソッド(method)と呼びます。例えば、C++言語ではC言語の構造体を拡張し、関数をメンバとして持てるようにしたオブジェクト型を作っています。
カプセル化のための可視性
メンバには責任を果たすために外部に公開(public)されるものとオブジェクト内に使用を限定する非公開(private)のものがあります。メンバには外部から見えるものと見えないものがあり、この性質を可視性と 呼びます。 可視性をどうするかは重要な問題です。 公開メンバは責務を担うものですから変更すれば他に影響します。しかし、非公開の部分は変更しても他に影響を与えません。
オブジェクトを旨く作ると中の複雑なものを見せずに公開されたメソッドのみで操作できる モジュール化された部品となります。例えばパソコンの画面に表示されるウインドウ、その上のボタンやテキストなどがオブジェクトで作られています。
※
人間は世界をモノの集まりとして認識し、その間の相互作用によって世界が動いてゆくと捉えています。例えば会社の業務などをオブジェクト指向分析によりモデル化するといった使い方
も行われます。
[目次]
オブジェクト指向の考え方では問題をオブジェクトとその間のメッセージ交換でモデル化します。ここで沢山のオブジェクトを分類するものとしてクラスと言う言葉が使われます。
例えば、会社の受付業務では受付係りの花子さん太郎さん、、2006年版電話帳などの資料、受付を訪れる客の次郎君などがオブジェクトであり、会話したり電話帳を調べたりして受付業務は行われます。ここで受付係り、客、電話帳といった分類がクラスです。クラス分けは色々ありえます、例えば社員と部外者とか、男と女とか、人と電話帳とか、問題領域の考え方でクラス分けは様々です。
C++言語では関数を要素に含めるた構造体を定義できるようにしました。このような構造体の変数は要素として変数(フィールド)と関数(メソッド)を持つのでオブジェクトを実装できます。ここで、オブジェクトの分類を考えると元の構造体の型の違いで分類できるので、拡張された構造体をクラスと呼びます。
型と変数に当たる関係を強調してクラス(class)とインスタンス(instance)という呼び方が使われます。 辞書を引くとそれぞれ次のような意味の言葉です 。
クラス(class):
辞書的には 分類されたもの、類、部類 の意味、インスタンス(instance):
辞書的には 事例、例、(事実を例証するための例)
インスタンスがオブジェクトに対応し、構造を示す拡張された構造体(クラス)が分類のクラスに対応します。
※本来はオブジェクトの分類を多様に考えることができるはずですが、 このようなクラス型の変数としてオブジェクトを実現しているために、多様な分類に対応するのは困難です。
java言語でもオブジェクトを実装する仕組みはC++と同様です。java言語のプログラムでも クラスにインスタンスの設計図である変数(インスタンスフィールド)や関数(インスタンスメソッド)の構造を記述 します。この設計図に従って作られたインスタンスが、オブジェクトに対応します。
C++との大きな違いは、javaではクラスの外に変数や関数を書くのを止めたことです。C++ではC言語と同様のmain関数をクラスとば別に書き、ここでクラスからインスタンスを生成して組み合わせて起動する部分を記述しています。java言語ではクラス自体が持つ変数(クラスフィールド)や関数(クラスメソッド)を使ってこれを行います。
オブジェクト指向モデルでもクラスに所属するオブジェクトが供用する変数や関数はクラスのメンバとすることがあります。例えば、太郎や花子が分類される人間クラスに総人口 を格納するフィールド(変数)あるいは総人口を計算するメソッド(関数)を用意するような 場合です。このようなメンバをインスタンスではなくてクラスのメンバとして持つことは解り易いと思います。
この仕組みを利用してjava言語ではクラスのメソッドとして起動用のmain関数を書くことになっています。プログラムを実行するときに指定したクラスのmain関数が起動の処理を行います。
※注意:
java言語では、クラス自体がオブジェクトとしての特性を持つことが可能なので、クラスにクラスフィールドやクラスメソッドのみを記述し、クラス間で互いに呼び出す形 のプログラムをつくることも可能です。 しかし、このようにクラスでオブジェクトを実装するのは本来の使い方ではありません。
[目次]
C言語プログラムの単位が関数であるのにた いしてjava言語ではクラスが単位になります。
print "Hello World!"
と一行で書けます。BASICでは、実行できるプログラムの最小単位は命令文です
/*CのHelloWorld*/
#include<stdiuo.h>
int main(void){
printf("Hello World!\n");
return 0;
}
となり、C言語の実行できるプログラムの最少単位は関数です。つまりHelloWorldを書き出す命令文単独では実行が許され ず、mainという名前の関数ブロックの中に入れてはじめて実行可能になります。
C言語ではプログラムに関数という構造を持ち込むことで、複数の処理をまとめて部品化を容易にしました。
/*javaのHelloWorld*/
public class HelloWorld{
public staitc void main(String args[]){
System.out.println("Hello World!");
}
}
クラスが実行プログラムの最少単位となっています。オブジェクトは複数の関数と変数を纏めることが出来るのでC言語に比べて規模の大きな部品を作ることが可能です。
上のHelloWorldプログラムではインスタンスの記述は何もありません。もしインスタンスにHelloと言わせたいのならクラスにインスタンスの設計図を記述し、クラスのmain関数でnewを使ってインスタンスを生成してから、インスタンスのメソッドを呼び出して仕事を依頼します。次のように記述します 。
インスタンスを使う利点は同じ形の複数のオブジェクトが用意できることなので英語と日本語で挨拶する2つのインスタンスを作ることにしました。
public class Hello
{
public static void main(String args[])
{
//まずはインスタンスを生成
Hello a=new Hello("Hello");
Hello b=new Hello("こんにちは");
//次にインスタンスのメソッド(関数)を呼ぶ
a.greetings();
b.greetings();
}
//ここからインスタンスの設計図 staticが付かないことで区別される
String msg; // 個々のインスタンスが持つ変数:フィールド
//クラスと同名で戻り値無しのメソッドはコンストラクタと呼ばれる
public Hello(String s)//インスタンス生成時に初期化で使われる
{
msg=s;
}
public void greetings() // 個々のインスタンスが持つ関数:メソッド
{
System.out.println(msg);
}
}
※ここでクラス名はなんでもかまわないが、publicなclassであることが必要、javaプログラムはクラスを指定して実行開始される為、クラスごとにmain関数を書くことが可能。クラス間のリンクは実行時に動的に行われる。
図の描き方はUMLと呼ばれるものがデファクトスタンダードになっています。ここでもできるだけUMLに従う書き方をすることにします。
HelloWorldプログラムを表すクラス図。
HelloWorld +main(args:String[ ]):void
クラスを四角形で表し、最上段にクラス名、中断にはフィールド、下段にはメソッドを記述します。下線の引かれたメソッドは、このメソッドがクラスのメンバーであることを示しています。また先頭の+記号は公開を意味する可視性の記号です。
Helloプログラムを表すクラス図。
Hello ~msg: String +main(args:String[ ]):void
+Hello(s:String)
+greetings()
ここではインスタンスのフィールドmsgとメソッドHelloとgreetingsが増えています。インスタンスのメンバーなので下線が在りません。「~」チルダのマークは可視性がpackageに公開の意味です。
※可視性はpublic,protected、package、privateと4種類在りますが、後で説明します。
Helloプログラムの main関数の流れは下の図のようになります。Helloクラスのmain関数はHelloのインスタンスを2つ生成しそのあとでそれぞれのgreetingsメソッドを呼 び出します。

この図はシーケンス図といい、上から下に時間が流れます。右上が折れた四角はコメントを記述するノート。
[目次]
main関数の記述は以下の2つの何れかです。
public static void main(String args[]){....}
static public void main(String args[]){....}
ここで、publicとstaticの順番はどちらでもかまいませんが、多くはpublicを先に書くようです。
仮引数の変数名は自由ですargsである必要はありません。
他の形でも関数の記述として文法ミスがなければコンパイルはできますが、クラスのmain関数をアプリケーションとして実行できません
例:
public class TestMain{
public static void main(){
for(int i=0;i<10;i++)
System.out.println(i);
}
}
この例はコンパイルできますがこのクラスをアプリケーションとして実行しようとすると
java.lang.NoSuchMethodError: main
Exception in thread "main"
の様にmainが無いと言われて実行できません。
(javaVMの中にはこの形を実行できる処理系もあるようです)
※C言語では関数は名前で区別されました。しかし、javaでは関数は名前と引数並びによって区別されます。この結果として、同じ名前でも引数の異なる関数を作ることが可能です。例えば下のようなプログラムを作ることが許されます。
public class Test2
{
public static void main(String a[]){
for(int i=0;i<10;i++)main();
}
public static void main(){
System.out.println("main()");
}
}
[目次]