プログラムを構造化する(Objective-Cでのオブジェクト指向のプログラミング)

Last-modified: 2014-01-26 (日) 00:21:31

プログラムを構造化する

オブジェクト指向のプログラムには、二種類の構造があります。ひとつは、クラス定義での継承階層の中に表れます。もうひとつは、プログラムが走っているとき、メッセージ送信のパターンにはっきりと表れています。これらのメッセージは、オブジェクトが連結したネットワーク*をあらわにします。(*a network of object connections)

  • 継承階層は、オブジェクトがどのようにタイプへ関連付けられているかを説明します。たとえば、水の利用をモデル化したプログラムで、蛇口パイプは同じ種類のオブジェクトだと分かりますが、パイプは開け閉めでき、パイプはほかのパイプと複数の接続ができます。この類似性は、蛇口パイプのクラスが1つの共有なスーパークラスから継承しているとすれば、プログラムのデザインへ取り入れられます。
  • オブジェクトを連結させたネットワークは、プログラムがどうやって動くのかを説明します。たとえば、機器オブジェクトはバルブへ水を要求するメッセージを送り、バルブパイプへメッセージを送ります。パイプ建物オブジェクトと会話し、この建物オブジェクトはすべてのバルブ蛇口パイプオブジェクトと会話しますが、機器とは直接会話しません。この方法でお互いか会話するのには、オブジェクトはお互いのことを知らなくてはなりません。機器オブジェクトはバルブオブジェクトとの接続を必要とし、バルブオブジェクトはパイプオブジェクトと、などなど。これらの接続がプログラム構造を定義します。

オブジェクト指向のプログラムは、オブジェクトの振る舞いと相互関係のパターンでオブジェクトのネットワークをレイアウトするのと、クラスの階層を整えることからデザインされます。その構造は、プログラムの活動の中と、その定義の中の両方に存在しています。

アウトレットコネクション

オブジェクト指向のプログラムをデザインする作業部分は、オブジェクトネットワークを整えることです。このネットワークは静的である必要はありません。プログラムが走る際に、動的に変わることができます。オブジェクト間の関係は、必要になったてから作られれば良く、割り振られた役割をふるまうオブジェクトの特色は時とともに変わることができます。しかし台本は無くてはなりません。

いくつかの結合は、まったく一時的であっても構いません。メッセージは、1つのオブジェクトと見なせるパラメータを含んでいることがあります。そのオブジェクトは、メッセージのセンダであることがおおく、このことからレシーバは送り先にオブジェクトと会話できます。送り先のオブジェクトがメッセージに反応するなら、レシーバはこのオブジェクトへメッセージを送ることができ、おそらくは自分自身をもしくはほかのオブジェクトを見分けて、こんどはオブジェクトが会話できます。このような結合は一時的なもので、メッセージの連鎖が続く間だけ存続します。

しかし、オブジェクト間の接続すべてが、実行中に取り扱えるわけではありません。いくつかの接続は、プログラムのデータ構造の中に記録されている必要があります。これをするには、さまざまな方法があります。オブジェクト結合を保持するテーブルが考えられますし、オブジェクトを名前で識別する何らかの機構かもしれません。しかし、最も簡単な方法は、どのオブジェクトも、それが会話をしなくてはならない他のオブジェクトを追跡するインスタンス変数を持つことです。これらインスタンス変数、メッセージへの出口を記録するのでアウトレット*と呼ばれます、はプログラム内のネットワークでオブジェクト間の主要な結合を定義します。(*outlet)

アウトレットインスタンス変数の名前が任意とはいえ、一般的にはアウトレットオブジェクトが果たす役割を反映しています。

Figure 4-1ではオブジェクトが4つのアウトレット、一人の代理人、一人の友達、一人の隣人、一人のボスをともなっている様子を図示しています。

それら部分の役割を果たすオブジェクトは、ときどき変わるかもしれませんが、その役割自体は変わらないまま残ります。

 
#aname: Invalid ID string: Figure 4-1

Figure 4-1  Outlets. (referred from Apple Developer Library)
Figure 4-1 アウトレット

 

いくつかのアウトレットは、オブジェクトが最初に初期化されたときにセットされ、その後は決して変わらないでしょう。またあるものは、他のアクションの結果として自動的にセットされるかもしれません。さらには、ある目的だけに提供されているメソッドを使って、自由にセットできるアウトレットもあります。

それらアウトレットが設定されても、そのインスタンス変数は、アプリケーションの構造を表へさらけ出します。アウトレットは、オブジェクトをコミュニケーションネットワークの中へ組み込みます。たとえば、水システムでのすべての要素は、物理的なつながりでリンクされており、個々のオブジェクトは、それが持つ一般的な関連性のパターンによってリンクされています。

外部的と内部的な接続

アウトレットコネクションは、オブジェクト間のさまざまな関連性をあらわしています。ときには、コネクションであり、アプリケーションの中にある多かれ少なかれ似ているパートナーと会話するオブジェクト間の結合で、どちらもすべき役割を持っており、お互いに支配関係にありません。たとえば、機器オブジェクトは、これに繋がったバルブを追跡するアウトレットインスタンス変数を持っているでしょう。

ときに、1つのオブジェクトが、他のオブジェクトの一部のように見えることがあります。たとえば、蛇口オブジェクトは、放出している水の量を量るメーターオブジェクトを持つでしょう。メーターオブジェクトには他の働きかけるオブジェクトがないので、蛇口オブジェクトからの要請があったときのみ役目を果たします。これは蛇口オブジェクトの内部的な部分と考えられます。これと対照的なのは、機器オブジェクトから外部的な接続は、バルブオブジェクトへつながっています。

同じようにして、他のオブジェクトを監視しているオブジェクトは、自分が責任を負うオブジェクトのリストを持っているでしょう。たとえば、プログラムにおいて、建物オブジェクトはすべてのパイプオブジェクトのリストを持っているでしょう。パイプオブジェクトは、建物オブジェクトの内部部品、そして建物に属していると考えられます。一方パイプオブジェクトは、お互いの間にある外部的な結合を管理しています。

内部的なアウトレットは、外部的なアウトレットへとは異なった振る舞いをします。あるオブジェクトが、開放されるかディスクのファイルへアーカイブ*されると、そのオブジェクトの内部的なアウトレットが指している別のオブジェクトは開放されるかアーカイブされなくてはなりません。たとえば、蛇口が開放されたら、メータは不要なものとなるので蛇口と同じように開放されるべきです。メータなしでアーカイブされた蛇口は、アンアーカイブ**されてもまったく使い道がありません(自分自身のために新しいメータオブジェクトが作られない限り)。(*archive: 凍結)(**unarchive: 解凍)

その一方で、外部向けのアウトレットは、高いレベルでのプログラムの成り立ちをあらわしています。これらアウトレットは、比較的独立したプログラムのサブコンポーネントの間のコネクションを記録します。機器オブジェクトが開放されても、これにつながっていたバルブオブジェクトは、それでも使用可能で、その場所に残ります。機器オブジェクトがアンアーカイブされると、他のバルブと繋ぐことのが可能で、以前に果たした役割と同じようなのを再開します。

オブジェクトネットワークをアクティベートする*

(*activate 活性化させる(他動詞))

ユーザインターフェイスを使ったインタラクティブなアプリケーションを書いているとしたら、キーボードとマウスによるユーザのアクションへ反応するでしょう。コマンドラインを使って、非常に大きな数をプログラムへ渡すと、その数の因数分解を試みるプログラムが開始します。あるプログラムは、電話線から受け取ったデータ、データベースから得られた情報、または機械的な動作の状況に関する情報といったものに、応答することがあるでしょう。

たびたび、プログラムは流れているイベント、何らかの種類の外部活動のリポート、によって活性化されます。ユーザインターフェイスを表示するアプリケーションは、キーボードとマウスからのイベントにより動作します。どのキーを押しても、またはマウスをクリックしても、アプリケーションを受けとり、応答するイベントが生成します。オブジェクト指向のプログラム構造(外部からの刺激に応答するよう作られた、オブジェクトネットワーク)は、この種のユーザ駆動型*アプリケーションへ理想的に適しています。(*driven, driveの過去分詞形、ドリブン)

集約と分解

デザイン作業における他の部分は、クラスの配置です。いつサブクラスを定義して既存のクラスへ機能を追加するべきなのか、いつ独立したクラスを定義するべきかを決めることです。極端な場合で何が起きるのかを予想することで、問題をあきらかにできます。

  • プログラムがたった1つのオブジェクトで構成されていると、考えることは可能です。たった1つのオブジェクトなので、メッセージは自分自身にしか送れません。したがって、ポリモーフィズムの恩恵も、たくさんあるクラスのモジュラー性も、または、相互結合したオブジェクトのネットワークを考慮に入れたプログラムデザインといった恩恵は受けられません。プログラムの真の構造は、クラス定義の中に隠れています。オブジェクト指向の言語で書かれていたとしても、このような構造ではオブジェクト指向であるとまったく言えません。
  • その一方で、何百の異なったオブジェクトを持ち、そのどれもがごく少数のメソッドと、用途が限られた機能性しか持たないといったプログラムを想像することができます。これもまた、プログラムが構造的であるとは言えず、この場合は、オブジェクトコネクションが迷路のようになっています。

明らかに、これら極端な例のどちらも避ける最良の方法は、オブジェクトはプログラムの中での実質的な役割を引き受けるのに十分な大きさで、その一方でよく練られて定義を維持するに十分な小ささであるのを維持することです。プログラムの構造は、オブジェクトのコネクションパターンを把握しやすいものであるべきです。

それにもかかわらず、クラスにもっと機能を加えたいのだが、または追加的な機能を取り出して、他に分けたクラス定義へ入れてはどうかといった疑問がしばしば湧きます。たとえば、蛇口オブジェクトは、ある時間に渡り、どれぐらいの水が使用したかを追跡する必要があります。そうするには、蛇口クラスに必要とするメソッドを実装するか、以前示したように汎用性があるメータオブジェクトを生成してその仕事を任せることができます。どの蛇口オブジェクトも、メーターオブジェクトへのアウトレット持っていますが、メーターは蛇口以外のどんなオブジェクトとも相互作用はしないでしょう。

しばしば、こういった選択は、あなたのデザインが目指しているゴールによって変わります。もしメータオブジェクトが他の状況でも使われる可能性があるなら、恐らくはまったく他のプロジェクトにおいて、調量*をする仕事を別個のクラスへ分ければ、あなたのコードはより一層と再利用性が上がります。もし蛇口オブジェクトができるだけ自己内包型にしたい理由があるのなら、調量の機能は蛇口クラスに加えるべきです。(*metering)

再利用可能なコードになるよう努めたり、たくさんのものを付けすぎたあまり大きなクラスとなってしまい、他の状況では仕えなくなってしまったりするのを避けるのは、一般的により好ましいことです。オブジェクトが要素としてデザインされていれば、より一層と再利用が可能なものとなります。1つのシステムまたは1つの条件下で動くオブジェクトといえど、他でもちゃんと働くべきです。

異なったクラスで機能を分け合って持つのは、必ずしもプログラムのインターフェイスを分かりにくいものにしません。もし蛇口オブジェクトがメータオブジェクトをプライベートに保持していたら、メーターオブジェクトのインターフェイスは、蛇口オブジェクトのユーザへ開かれたものである必要は無いでしょう。メータは、蛇口の他のインスタンス変数と同じように、できるだけ隠しておいた方がいいでしょう。

モデルとフレームワーク

オブジェクトは状態と振る舞いを結合させているので、現実社会のモノと似ています。オブジェクトは現実のモノと似ているので、オブジェクト指向のプログラムをデザインするのは、現実のモノについて考えるのに非常によく似ています。何をするのか、どうやって動くのか、そして、どうやって他のものとつながるのか。

オブジェクト指向のプログラムをデザインするときは、実のところモノがどう動くのかをあらわしたコンピュータシミュレーションを組み立てていることです。オブジェクトネットワークは、見た目も振る舞いも実世界のシステムのモデルに似ています。オブジェクト指向のプログラムは、実世界にはその相対物*がないにもかかわらず、なにかのモデルであるとも考えられます。(*counterpart: カウンターパート)

モデルのどの要素も、ーどの種類のオブジェクトもー、その振る舞い、責任、そして他の要素との相互関係の観点から説明されます。オブジェクトのインターフェイスは、データではなくてメソッドに備わっているので、デザインの過程は、システムの要素がデータの中でどう表されるかではなく、その要素が何をしなくてはならないかを考えることから開始できます。オブジェクトの動作が一度決まれば、適切なデータ構造を選べますが、これは実装段階で行うべき事柄で、最初のデザイン段階でやることではありません。

たとえば、水利用プログラムにおいて、蛇口のデータ構造がどのようなるか決めることから始めるべきではなく、蛇口オブジェクトにして欲しいこと、パイプとつながる、開け閉めができる、水量を調節するといったことから始めるべきです。したがって、デザインはデータ選択の出だしに捕らわれてはいけません。まず最初に動作を決め、あとからデータを実装すべきです。データ構造の選択は、デザインに影響をあたえることなく、何度でも変えられます。

オブジェクト指向のプログラムをデザインするのに、大量のコードを書く必要は必ずしもありません。クラス定義を再利用できるということは、他のプロジェクトで生成されたクラスをつかえるので、プログラムを大きく作っていく機会はたくさんあります。誰かが定義したクラスだけで、興味深いプログラムを組み上げることさえできます。クラス定義の詰め合わせが大きくなれば、そこから選べるたくさんの再利用した部品を持つことになります。

再利用可能なクラスは、多くのソースから抜き出せます。しばしばプロジェクト開発から再利用が可能なクラス定義がもたらされ、企業に属する開発者はこれらを売りに出します。オブジェクト指向のプログラミング環境には、一般的にクラスライブラリがついています。Cocoaライブラリには、優に200以上のクラスがあります。それらの中には、基本的な機能を提供するものがあります(ハッシュ、データ保管、リモートメッセージング)。もっと特定な機能のもあります(ユーザインタフェイス、ビデオディスプレイ、サウンド)。

一般的に、ライブラリクラスの1つのグループは、特定のプログラム構造を定義するのに共同して仕事をします。これらクラスはソフトウエアフレームワーク(またはキット)を構成しており、バラエティに富むさまざまなアプリケーションを組むのに使うことができます。1つのフレームワークを使うと、それが提供し、またデザインをそれに合わせたプログラムのモデルを手に入れられます。フレームワークを使うのは

  • フレームワーククラスのインスタンスを初期化してアレンジする
  • フレームワーククラスのサブクラスを定義する
  • フレームワークで定義されたクラスと一緒に作業をする新しいクラスを定義する

上記どの方法でも、プログラムをフレームワークに合わせるだけでなく、汎用なフレームワーク構造をアプリケーションの特別な目的に合わせる必要があります。

本質的に、フレームワークはあなたのプログラムのためにオブジェクトネットワークの部分を準備して、クラス階層の部分を提供します。あなたのコードは、フレームワークを起点としてプログラムモデルを完成させます。

Referred from

Object-Oriented Programming with Objective-C, Structuring Programs

 

Copyright

Copyright © 201 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2010-11-15