しいしせねっと Half Banner_haruhb
[しいしせねっと][プログラミング基本編] [エージェントとか]

プログラミングオブジェクト指向編

このページの目次

オブジェクト指向ってなんや? そんなお悩みが多いようでございます。
基本的な所がわかっているプログラマ向けに、オブジェクト指向の簡単な解説をしませう。
抽象的な説明と、具体的な説明、どちらがいいかな・・・。
C/C++、Java的な解説となりまする。

オブジェクト指向のはじまりは、C言語でいうところの構造体 (struct)、データベースでいうところのテーブルのようなものです。
C言語の中では、構造体は、ひとかたまりのデータを表しますね。
構造体をつくるより、各項目別に変数の配列を作ればいいではないかと思っていたのは私だけではないと思いますが、とりあえず、メモリーの使い方なんてコンパイラがある程度最適に処理してくれるので、構造体使っとけということらしいです。

オブジェクト指向がわかりにくいのは、いろいろな事象で使われてきたものを総まとめして、同じところをまとめたのがオブジェクト指向というものになったから、かもしれません。具体的な例をいくつか見ていくと、共通点がいくつか見えてきます。

オブジェクト指向の基礎は、C言語の構造体なのです。これを、あれやこれやと拡張していくと、オブジェクト指向が少しずつ見えてきます。

構造体の例

struct music {
int id;
char name[256];
long time[10];
} musiclist[10];

こんな感じだったでしょうか。

構造体は、データを追加したくなったとき、少し違う構造体を最初から作り直さなければいけませんでした。
クラスは、継承することで、どんどん拡張することができるような構造になっています。

クラスと構造体の違いは、いくつかあります。

メソッド

この構造体にアクセスするプログラム(関数など)をつくるわけですが、そのなかでも特定の構造体に対応する関数を、構造体とまとめて定義できるようにしてしまおう、というのがオブジェクト指向のはじまり的なところではないでしょうか。しかし、複数の構造体を扱うためには、1つの構造体にプログラムをつけるわけにはいきませんから、従来のようなプログラムをあわせてつくるとよいということで、C++の場合はぐちゃぐちゃとしたものに仕上がっています。とりあえず、これでメソッドのはじまりですかね。

構造体というものは、ある程度役割がきまっていたりするので、メソッドを作っていくと、定義したメソッド以外の方法でデータを変更されたり参照されると都合の悪いことがでてきます。そこで、データを外から見えなくしてしまう、ということをしてしまったのです。これは、単純なプログラムでは手間にしかならないと思いますが、大規模なシステムをを作る上では、間違いを減らすという役割を持っていたりいなかったりします。また、データを隠してしまうことで、違う形の内部構造に変更することが楽になります。普通の構造体と、データベース上にアクセスするクラスなどを全く同じように扱えるようになりました。

継承

継承という考え方によって、基本的な部分と拡張された部分を別々に作り、同じようにして使うことができるようになりました。

それからそれから

デバイスとのやりとりも、標準的なクラスの形を決めておくことで、様々なデバイスにおなじプログラムで対応することができるようになってきます。

以下、メモ

なぜ new なのか

String name = new String(名前); の new とは何なのか、ということがわからない人のために何か書いてみるかもしれない。オブジェクトと変数の違いについて、コンストラクタの役割についてなど。このためには、C言語のポインタの概念を知っているのがいいのですが。

オブジェクトには、メソッドなどがありますが、基本的にはデータです。データを扱うときには、メモリ空間が必要です。

関数、メソッドの分割

1つの関数、メソッドの中に多くの処理を書きすぎると、何をしているのか一目ではわからなくなってしまいます。
適切な大きさで、別の関数・メソッドに分けることは、オブジェクト指向でも同様に必要です。

インスタンス化の難しさ

クラスが違うと、コンストラクタは別々になってしまいます。サブクラスを多くそろえて、その中から選択してインスタンスを生成したい場合に、ひとつひとつのクラスに対してコンストラクタを呼び出す処理を書かなければいけません。
そこで、コンストラクタの羅列にならないようにするには、どうすればいいでしょう?

オブジェクトは所詮オブジェクト

オブジェクトは、それ自体が存在するだけでは、何の動きもしません。
やはり、main から、順番に操作する手続きは必要です。

クラス/構造体作りの基準

クラスを作る/分けるときは、1つのメソッドで処理しきれない変数の固まりができたときなどにしましょう。
1つのメソッド内だけで完結するものにわざわざ新しいクラス/構造体を用意する必要はありません。
逆に、設計段階でクラス分けする場合は、

static メソッドの使い道

static メソッドは、main 等々のインスタンスに依存しない、主に管理的部分で使われます。
どういった場面でstatic にし、またどういった場面でstatic にしないのがよいのでしょうか。
main メソッドと、そこから派生する処理は、基本的に全て static にするのがいいかもしれません。main からの処理は、どこかのクラスに依存した処理ということは、ほとんどないはずです。オブジェクト指向を意識しすぎてしまい、手続き型の処理を忘れて深みにはまってしまうことがあります。class のメソッドの処理と main 処理は、同じ関数やメソッドの型に見えても全く性格の異なるものです。mainは便宜上関数やメソッドの形式をとっているだけだと思っておけばいいでしょう。

複数のクラスからインスタンスを選択生成する場合。
コンストラクタがわりに、static メソッドを使う。

super class でインスタンス化して、それをサブクラスに拡張していくことは可能か?
Javaではできないが、メソッドのみの追加であれば、これは可能なはずである。が、メソッドを追加するだけなら機能の違いとしてあらわれないので、サブクラス化する意味があまりない?
ふるまいの異なるサブクラスが定義できるので、サブクラス化する意味はある。
データを保持したまま別のクラスのインスタンスになるのは、難しい。変更できたとしても、参照構造が追いつかない。
メソッドはクラスと一体になっていてよかったのだろうか?

オブジェクトとイベントとスレッド

オブジェクト指向というと、イベント駆動型の開発パターンが多くなってきます。これは、フレームワークなどということができます。

プログラムをオブジェクトとそれを呼び出すフレームワークというものに分け、あらかじめ用意されたフレームワークにしたがってオブジェクトを作ることで、プログラムが動作します。

従来はというと、OSからアプリケーションを実行するときにはmain関数だけを呼び出して、あとはアプリケーションからOSの機能を使うという形でした。

スレッド。これは、mainからはじまるプログラムの実行順序みたいなものです。これが、スレッドという形でmain以外からもはじめられるようになっています。

しかし、基本的にスレッドは親子関係をきちんとして設計し、やたらとスレッドを増やしたりしない方がいいかもしれません。

イベント1つ1つに対してスレッドを生成すると、スレッド同士の衝突がおこります。たとえば、メモリ、変数の中身を意図しない順番で変更してしまうなどです。スレッドは各オブジェクトに1つ、または1つのまとまった機能単位で1つのスレッドを使うようにします。ゲームなどでもキャラクタ1つ1つにスレッドを与えるよりは、全体で1つのスレッドとして管理されたタイミングで実行する方がいいです。

オブジェクトと制御コード

手続き型言語では、プログラムとデータ(構造体など)は、はっきり分けられていたのですが、オブジェクト指向は、一つのクラスの中に、データとプログラムが混在しがちです。Javaはすべてクラスでできている、というのも誤解のもとだったりします。

クラスやオブジェクトという考え方を進めすぎると、手続き型の考え方が欠落してなんでもデータ用のクラスの中に詰め込もうとしてしまいます。

しかし、データ用の構造体と、制御用のシステム側のクラスは、別々のものと考えておく方がよいです。C言語などの非オブジェクト指向のところから、この基本の部分は変わりません。Javaであれば、データはBeanやEJB(エンティティBean)などの形で実装します。それを制御するコードはServlet、セッションBean、mainメソッドとそのサブルーチンなどの形で実装します。スレッドなどの処理も、制御コード側で対応します。

データ用のクラスは、広範囲のシステムから利用できるように意識して作ります。アクセサなどというようなget/setメソッドを作るのが流行りのようです。そのデータ固有の機能はメソッドとして実装してもいいのですが、他のクラスとあわせて機能を実現したり、システム全体にかかわるような大規模なロジックは、入れないようにしましよう。

制御用のクラスは、対応関係のシステムとのインターフェースを決めます。複数のデータをあわせて処理をする場合などは、特定のデータクラスにメソッドとして実装するのではなく、機能用のクラスを別途用意して、そこに実装します。データ依存度の高い機能は、制御クラスにする必要はありません。たとえば、複数のインスタンスをまとめて処理するような場合は、データ用クラスにstaticメソッドとして実装する方法もありますが、複数のクラスが関係したり、特定の機能を実現するためだけに依存するような処理の場合は、それをまとめるクラスにメソッドを実装します。

データクラスには永続化という課題があります。これは、Stringクラスのように、プログラム中で使うだけでなく、ファイルなどに長期間記録するデータを使うためのものもあります。永続化するものは、EJB、シリアライズ、ファイルやデータベースなどがあります。これらの機能は、データ用クラスではなく、データ管理用のクラスを別途用意しておくのがよいかもしれません。

この妨げになるような機能は、データ用クラスに含めない方がよいです。

データの参照

プログラム中にデータを格納する場合、データベースやファイルなどを利用することがあります。この場合、データベース中とメモリー中の2つのデータが存在することもあります。メモリ中ではインスタンスとして存在するので、参照やポインタという形で解決することができるのですが、参照関係が複雑になってくるとメモリの開放するタイミングなどが難しくなることがあります。この場合は、データの管理コードなどを持たせ、そのデータを使って間接的に参照するようにした方がいいのかもしれません。Javaでは弱参照用のクラスもありますが、あまり頼らない方がいいのかもしれません。

たとえば、Webでの販売システムを設計するとします。

このなかで、商品はどのように作ればよいのでしょうか? 商品の存在する場所によって複数の形体が考えられます。

これらはすべて同じクラスまたはインスタンスとして扱えるでしょうか?

セキュリティ的意味

2003.11.23

オブジェクト指向とは、データに対する操作に制約を持たせることです。

クラスは、メソッドで内部の情報を隠蔽できますが、どこからアクセスする場合も同じようにしか制限できないのでしょうか。

クラス内からのみアクセスできる、外部からもアクセスできる、など、いくつかの制限ができます。それでもクラスの外からみえる情報はどこから見てもおなじです。

これは、継承した上位クラスの型にキャストするということで、一部のメソッドのみを利用させることができます。でもこれは元の型にキャストされてしまえば元の機能を全て使えてしまいます。Javaのインターフェースなどでは、このようなことができますね。

アクセスのためのクラスを用意する (クラス型変換?)

かなり高度な内容になってきてしまいましたが、相手によってアクセスを制限するためには、それぞれ別々のインターフェースを用意し、アクセスのためのクラスを実装します。EJBなどが一部この形に似たことをしていますが、アクセス制限を意識しているのはセッションBeanという形で実現されているようです。

IntelのCPUのマニュアルなどを見たことがある人は、特権レベルというのが書かれていたのを知っているかもしれません。あれを、クラスの形で実装することも可能ではないでしょうか?

いちばん内側が特権レベル0、全ての機能が使えます。外側が特権レベル1、2、3と続きます。1つのインスタンスを使って別々のインターフェースを公開するのは、Javaの仕様では困難です。特権レベル0のインスタンスを参照するレベル1から3のクラスをつくり、レベルに応じたおなじインターフェースを継承し、生成できるようにするのがよいでしょうか。

class A implements IfA0,IfA1,IfA2,IfA3A,IfA3B;
class A1 implements IfA1,IfA2,IfA3A,IfA3B;
class A2 implements IfA2,IfA3A,IfA3B;
class A3A implements IfA3A;
class A3B implements IfA3B;

対象別に、A3AとA3Bのような分け方もできまする。あとは、生成手順を工夫すれば、IfA0を実装した実体を隠しつつ、ということもできるでしょうか。

全くおなじインスタンスではないので、比較演算子を工夫する必要もあります。

これはまだ考えているところです。

Get Thunderbird


[しいしせねっと]