オブジェクト指向開発〜設計・実装その1

設計・実装について、はじまり。

はじめに

オブジェクト指向では、分析から実装まで大きな視点の変換が無く、シームレスに連続している。分析段階で登場するクラスという単位は、実装段階までクラスのまま。そのため、問題領域に変更が生じた場合でも、変更箇所を用意で、誤りも混入しない。
オブジェクト指向に置ける『クラス』というものは、プログラム的な単位ではなく、分析から実装まで一貫して登場する、プログラム・ソフトウェアという存在そのものの構造の基幹をなす単位と言える。
アプリケーションが稼働するときに不可欠なウィンドウやダイアログボックス、データベースなどは問題領域を観察しているだけでは発見できない。これらのクラスが登場し発見されるのは、『設計工程』になる。設計工程はシステムを動かす事、そのためにはアプリケーションに何が必要かを中心に考える。

オブジェクト指向設計

オブジェクト指向において、『オブジェクト指向設計』という統一的な設計歩法は存在しない。分析から設計、実装までがシームレスに連続しているため、設計工程に特化した新しい概念が存在しにくい構造であると言える。

問題領域以外のクラス

Doc/Viewアーキテクチャ、MVC(Model-View-Controler)、OOSE(Object-Oriented Software Engineering)といった分類方法がある。Doc/Viewアーキテクチャでは、アプリケーションを問題領域のクラス(Document)と表示を受け持つクラス(View)とで構築しようと言う方法。Modelが問題領域に当たるMVCという方法。OOSEでは、『実体オブジェクト(問題領域)』、『インターフェースオブジェクト』、『制御オブジェクト』といった分類を行う。

OOSEでのクラスの分類

OOSEという手法では、アプリケーションは「実体」「インターフェース」「制御」の3種類のオブジェクト(クラス)の集合になっていると定義している。

実体クラス
仕様上に名詞として登場する様な、具体的なクラス。つまり、問題領域のクラスの事。
インターフェースクラス
システムとオペレータとの接点で入出力を司るオブジェクト。
制御クラス
実体クラスとインターフェースとを繋ぐ役割を果たすクラス。

より実践的な分類

一般的に設計時に現れるクラスには、以下の様なクラスがある。

  • ユーザインターフェースなどを司る、問題領域とオペレータの間を介在するクラス
  • ある特定の処理を専門に行う、関数の様なクラス
  • 問題領域のクラスを拡張し、そのアプリケーションに特有の処理を追加したクラス

ユーザインターフェースに関連するクラス

ユーザインターフェース(UI)に関連するクラスとは、オペレータとシステムの境界に存在し、外界とシステムとのやり取りを司るクラス。現実的に、MFC やOWLなどといった、UI用のクラスライブラリが用意されているため、UIのクラスについてはほとんど考える必要はない。
問題は、提供されるライブラリをどのように利用して、UIを設計するか。MFCヤOWLには多くのUI用クラスが用意されているが、あくまで基底クラスレベルで提供されているにすぎないため、実際にはこれらを派生させて実際にUIを実装するクラスを作らなくてはならない。

オブジェクト指向とUI

一般的にUIの設計は、「人に優しく」なければならず「センスが必要」なため、理系出身の技術者には出来ない物だと言われる事があった。しかし、UIはその製品のユーザビリティを決定する重要な部分で、その根拠を「優しさ」や「センス」に求めて設計するなどという事は、根本的に間違ったアプローチ。

UIの具体的な例

最近主流になっているUIは、実はオブジェクト指向という概念とは切っても切れない関係がある。
ワープロソフトについて考えると、オブジェクト指向的には、文書データが『属性』、アプリケーションが『操作』、それらをまとめて『オブジェクト』と位置づける事が出来る。つまり、OSレベルから見ると、アプリケーションとデータが組み合わさって、それをひとつのオブジェクトと考える事が出来る。
ウィンドウを「オブジェクト指向に基づいて構築されたアプリケーション」という視点で考えると、問題領域の暮らすインスタンスの属性の、オペレータに対しての提示となる。メニューやツールバーは、オペレータがそれらの問題領域のクラスに対して『操作』する窓口。この様に考えると、プログラムはそれ自体がオブジェクトと言える。

従来のUIとオブジェクト指向UI

かつて、DOS上のファイル管理ソフトウェアマーケットでは、2つのソフトウェアに指示が大きく分かれていた事があった。それぞれ操作方法が異なり、1つは処理を選んでから対象になるファイルを選択する方式、もう1つはファイルを選択した後でその処理を選ぶという方式。
現状から分かる様に、後者の方法が結果的に支持された。これは、その方法がオブジェクト指向的だったからという事があると言える。

特定の処理を行うクラス

実際のシステムが稼働する場合、非常に複雑な処理を必要とする事がある。この場合に、複雑な処理自体を行うクラスとしてパッケージ化する。そして、問題領域のクラスが、その複雑な処理を依頼する事になる。受けた操作を別のクラスに対して依頼する事を『委譲』と言う。

クラスのアプリケーション依存

問題領域のクラスは、基本的には処理系独自の処理や、アプリケーション独自の仕様に依存するべきではない、という前提がある。
設計段階では、プログラムを動かす事が最終目的。処理系に依存したクラスもアプリケーションに依存したクラスも数多く登場する。問題領域のクラスも、そのような処理を受け持つクラスと、全く関わりを持たない事は簡単ではない。クラス自体がアプリケーションや処理系から独立していても、そのようなクラスと関連を持った時点で処理系に依存したクラスとなってしまう。問題領域クラスを他のアプリケーションで利用すると、そのクラスと関連のある他のクラスまで引きずられるため、この『他のクラスとの関連』が、クラスの再利用性を阻む大きな要因になる。
継承は、どのようなクラスを派生させても、基底クラスはその影響を受けない。よって、アプリケーションの仕様に依存したクラスを継承によって作成しても、分析段階で導きだされたクラスはその影響を受けず、純粋にその概念を実現したままでいられる。そして、設計レベルのクラスだけが、アプリケーション用に作られた、同じく設計段階で現れたクラスと関連を持つ事になる。処理系依存の部分を派生クラスで管理すれば、派生クラスだけを切り離し、その処理系に依存したクラスを別の派生クラスとして作成する事が容易になる。

サブシステム分割

設計時に行わなければならない、という割れている物のひとつにサブシステム分割がある。オブジェクト指向においてサブシステム分割はクリアになっていない。大雑把に、従来と同様の機能単位にクラス同士を集める事と言える。
ひとつの指針として、関連の多い少ないという基準がある。サブシステム内のクラス同士は関連が多く、サブシステムをまたがったクラス間の関連は少ない。

クラスの詳細化

操作の詳細化
操作は実装的にはメンバ関数で、パラメータや返り値がある。どのような情報が必要で、その情報がどのような形かを考えなくてはならない。また、アクセス指定子についても決定する必要がある。
属性の詳細化
型とアクセス指定子を決定する。基本的にアクセス指定子はprivate。
関連の詳細化
関連は通常、クラスのデータメンバに関連先のポインタ(参照)を用意し、関連するインスタンスを保持になる。中には、操作を行っている間だけという一時的な関連もあり、その場合はメンバ関数のパラメータとしてインスタンスが渡されるという形で実現する。この関連は使用関連と呼ばれる。
関連は単方向関連か双方向関連かも検討する必要がある。関連を張ると、張った側のクラスは相手のクラス無しでは動作できなくなるため、独立性が失われる。クラスの再利用性に関わる問題のため、その都度最適な解法を決定する必要がある。
1対多関連の場合など、複数のインスタンスを相手にする場合そこにある種のアルゴリズムが存在する場合がある。インデックスアクセスでなく順序アクセスが望ましい場合は、配列でなくリストやバイナリツリーなどのアルゴリズムを用いた方がいい場合が多い。
プロフィール

このブログ記事について

このページは、koshigoeが2005年12月18日 21:46に書いたブログ記事です。

ひとつ前のブログ記事は「Google Personalized Homepageのwidgetが作れない〜訂正」です。

次のブログ記事は「バグから得るもの」です。

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。