|
|
Log In | Not a Member? |
Contact ADC |
| < Previous PageNext Page > |
Application Kitなどのフレームワークによって定義されるプログラムモデルは、汎用的なため、数多くの種類のアプリケーションで共通に利用できます。モデルが汎用的なので、当然ながらフレームワークのクラスによっては、抽象的であったり意図的に不完全であったりします。多くの場合、クラスでは作業の大部分を低レベルの共通のコードの中で実行しますが、動作の大きな部分を未完成のままにするか、安全ながらも汎用的な“デフォルト”の形に完成させます。
アプリケーションではしばしば、スーパークラスにおけるこれらの隙間を埋め、フレームワーククラスで欠けている部分を供給する、サブクラスを作成する必要があります。サブクラスは、アプリケーション固有の動作をフレームワークに追加するための主要な手段になります。カスタムのサブクラスのインスタンスは、フレームワークで定義されているオブジェクトのネットワークの中に位置を占め、ほかのオブジェクトと連携して動作する能力をフレームワークから継承します。たとえば、NSCellのサブクラスを作成すると、フレームワークに定義されているNSButtonCellやNSTextFieldCell、その他のセルオブジェクトの場合と同様に、この新しいクラスのインスタンスをNSMatrixオブジェクトに表示できます。
サブクラスを作成する際の主な作業の1つは、スーパークラスで宣言されている(またはスーパークラスで採用されているプロトコルにある)特定のメソッド群を実装することです。継承したメソッドを実装しなおすことを、そのメソッドをオーバーライドすると言います。
メソッドのオーバーライドを必要とする場合
サブクラスの作成を必要とする場合
フレームワーククラスに定義されているメソッドのほとんどは十分な実装を持っています。これらは、メソッドをそのまま呼び出して、クラスが提供するサービスを利用できるようにするために存在します。このようなメソッドをオーバーライドする必要はほとんどなく、またオーバーライドするべきではありません。フレームワークは、それらのメソッドが実行する処理をそのまま利用します。それ以上のことも、それ以下のことも求めません。それ以外の場合は、メソッドをオーバーライドできますが、そうするべき本当の理由はありません。フレームワークが提供するメソッドは、十分な仕事をします。しかし、strcmpを使用せずに、独自の文字列比較関数を実装するのと同様に、必要であれば、フレームワークのメソッドをオーバーライドすることも選べます。
一方で、フレームワークメソッドによってはオーバーライドを前提とするものがあります。これらは、プログラム固有の動作をフレームワークに追加できるようにするために存在します。多くの場合、フレームワークによるそれらのメソッドの実装は、アプリケーションにとって意味のあることをほとんど、あるいはまったく何もしませんが、ほかのフレームワークメソッドから発信されるメッセージの中で呼び出されます。このようなメソッドに実質的な機能を与えるためには、アプリケーションで独自にメソッドを実装する必要があります。
サブクラスの中でオーバーライドするフレームワークのメソッドは、開発者が自分で呼び出すことは(少なくとも直接には)ありません。単純にメソッドを実装しなおして、残りの処理はすべてフレームワークに任せます。実のところ、アプリケーション固有のメソッドを記述する可能性が高いほど、それを自分のコードの中で呼び出す可能性は低くなります。これには確かな理由があります。一般に、フレームワーククラスでパブリックメソッドが宣言されているのは、開発者が次の2つのいずれかを実行できるようにするためです。
メソッドを呼び出して、クラスが提供するサービスを自分で利用する。
メソッドをオーバーライドして、フレームワークによって定義されるプログラムモデルに、自分のコードを組み込む。
場合によっては両方に該当するメソッドもあります。呼び出せば有用なサービスを提供する一方で、必要があればオーバーライドすることもできるメソッドです。しかし、一般に、呼び出すことができるメソッドの場合、そのメソッドはフレームワークによって十分に定義されており、コードの中で定義しなおす必要はありません。サブクラスの中で実装しなおす必要があるメソッドの場合は、フレームワークにおいてそのメソッド固有の役割が決まっているので、メソッド自体が適宜呼び出されます。Figure 3-2は、この2種類のフレームワークメソッドを示します。
Cocoaフレームワークを使用したオブジェクト指向プログラミングの作業の大部分は、フレームワークによって組み立てられるメッセージを通じてプログラムが間接的にのみ使用するメソッドを実装する作業です。
フレームワークメソッドの中には、十分に実装されていて、ほかのフレームワークメソッドから呼び出されることを前提としているものがあります。言い換えれば、これらのメソッドは実装しなおすこともできますが、多くの場合、コードの中で呼び出すことはありません。この種のメソッドは、プログラム実行中の何らかの時点で、ほかの何らかのコードで必要となる、何らかのサービス(データや動作)を提供します。これらのメソッドがパブリックインターフェイスに存在する唯一の理由は、必要があればオーバーライドできるようにするためです。これらは、フレームワークで使用されているアルゴリズムを独自のものに置き換えたり、フレームワークのアルゴリズムを変更または拡張したりできる機会を提供します。
この種のメソッドの例としては、NSMenuViewクラスで定義されているtrackWithEvent:があります。NSMenuViewでは、メニューの追跡や項目の選択の処理といった直接的な要件を満たすためにこのメソッドが実装されていますが、別の動作が必要な場合はオーバーライドすることもできます。
また、別の種類のメソッドとして、属性がオンかどうかや、特定のポリシーが有効かどうかといった、オブジェクト固有の判定を行うメソッドがあります。フレームワークにはこの種のメソッドについて、1つの方法で判定を行うデフォルト版が実装されており、別の判定を必要とする場合は独自版のメソッドを実装する必要があります。ほとんどの場合、実装では単純に、YESまたはNOを返すか、デフォルト以外の値を計算するかになります。
この種の典型的なメソッドとしては、NSResponderのacceptsFirstResponderがあります。ビューにはacceptsFirstResponderメッセージが送られ、特にキー操作やマウスクリックに応答するかどうか確認されます。デフォルトでは、ほとんどのビューは入力を受け付けないため、NSViewオブジェクトはこのメソッドに対してNOを返します。しかし、入力を受け付けるビューもあり、そのような場合はacceptsFirstResponderをオーバーライドしてYESを返すようにします。
オーバーライドを必要とするメソッドのうち、フレームワークにおけるメソッドの実装が行うことを置き換えるのではなく、何かを追加する対象となることのみを目的とするものがあります。このようなメソッドのサブクラス版では、スーパークラス版の動作を強化します。この種のメソッドの1つをプログラムで実装する際は、オーバーライド対象メソッドを必ず組み込むことが重要です。これには、メッセージをsuper(スーパークラス)に送って、フレームワークに定義されているほうのメソッドを呼び出します。
多くの場合、この種のメソッドは継承チェーン内のすべてのクラスにおいてオーバーライドすることが期待されます。たとえば、自分自身をアーカイブするオブジェクトは、NSCodingプロトコルに準拠する必要があり、initWithCoder:メソッドとencodeWithCoder:メソッドを実装する必要があります。しかし、クラスで自身のインスタンス変数に固有のエンコードまたはデコードを実行する場合は、その前にスーパークラス版のメソッドを呼び出す必要があります。
サブクラス版のメソッドで、スーパークラスの動作を“再利用”し、最終結果に何らかの(いかに小さくとも)追加を施したい場合があります。たとえば、NSViewのdrawRect:メソッドにおいて、何らかの複雑な図形を描画するビュークラスのサブクラスで、図形の周りに境界線を描きたい場合は、先にsuperを呼び出します。
フレームワークメソッドの中には、実行時やコンパイル時のエラーを防ぐ目的で、まったく何も実行しないものや、差しさわりのないデフォルト値(selfなど)を返したりするものがあります。これらのメソッドはオーバーライドすることが前提となっています。これらのメソッドが実行する処理は完全にプログラム固有です。したがって、フレームワークではメソッドの基礎的な処理さえも定義できません。superにメッセージを送ってフレームワークのメソッド実装を組み込む必要はありません。
サブクラスでオーバーライドするメソッドのほとんどは、この部類に該当します。たとえば、NSDocumentのメソッド(特にdataOfType:error:とreadFromData:ofType:error:)は、ドキュメントベースのアプリケーションを作成する場合にオーバーライドが必要になります。
メソッドのオーバーライドは必ずしも面倒な作業ではありません。多くの場合、コードに1、2行加えるだけという方法で、メソッドを慎重に実装しなおすことで、スーパークラスの動作に大幅な変更を加えることができます。また、独自版のメソッドを実装する際は、完全に自分だけの力で作業をこなさなければならないということはありません。Cocoaフレームワークによってすでに提供されているクラス、メソッド、関数、および型を利用できます。
クラスのどのメソッドをオーバーライドする必要があるかを知ることは重要ですが、それと同様に、その決定の前に、どのクラスを継承するのかを明らかにすることも重要です。継承元の決定は、明白な場合もあれば、簡単にはいかない場合もあります。ここでは、決定の際に参考となるデザイン上の考慮事項をいくつか示します。
まず、フレームワークを理解することです。個々のフレームワーククラスについて、その用途や機能に慣れるようにしてください。自分が必要としていることを行うクラスがすでに存在するかもしれません。また、自分が必要としていることをほぼ行うクラスを見つけることができれば幸運です。そのクラスは、カスタムクラスのスーパークラスの候補になります。サブクラス化は、自分のニーズに合わせて既存のクラスを再利用し、カスタマイズする作業です。継承したメソッドを1つだけオーバーライドし、元の動作とはわずかに異なる動作を実行するように手を加えるだけで、サブクラス化が済む場合もあります。あるいは、1つまたは2つの属性を(インスタンス変数として)スーパークラスに追加した後、それらの属性にアクセスして操作するメソッドを定義し、それらをスーパークラスの動作に組み込むことで、サブクラス化を行う場合もあります。
それ以外に、自分のサブクラスをクラス階層のどこに組み込むのが最適かを決めるうえで役立つ考慮事項があります。自分が作成しようとしているアプリケーション、またはアプリケーションの一部は、どのような特徴を持つものでしょうか。Cocoaのアーキテクチャの中には、独自のサブクラス化要件を要求するものもあります。たとえば、複数の文書を扱うアプリケーションの場合は、CocoaのドキュメントベースのアーキテクチャにおいてNSDocumentをサブクラス化する必要があり、おそらくほかのクラスもサブクラス化が必要になります。アプリケーションをスクリプト対応にする(つまり、AppleScriptのコマンドに対応できるようにする)には、NSScriptCommandなどのスクリプトクラスの1つをサブクラス化しなければならない可能性があります。
サブクラスのインスタンスがアプリケーションの中で果たす役割についても、考慮する必要があります。Cocoaの重要なデザインパターンであるModel-View-Controlデザインパターンでは、役割がオブジェクトに割り当てられます。これらは、ユーザインターフェイスに表示されるビューオブジェクトであったり、アプリケーションデータ(およびそれらのデータに作用するアルゴリズム)を保持するモデルオブジェクトであったり、ビューとモデルオブジェクトとの間を仲介するコントローラオブジェクトであったりします(詳細については、“「Model-View-Controllerデザインパターン」”を参照してください)。オブジェクトがどのような役割を果たすのかを知ることによって、どのスーパークラスを使用するかを決める際に、その選択肢を絞ることができます。クラスのインスタンスが、独自のカスタム描画とイベント処理を行うビューオブジェクトである場合は、おそらくNSViewからクラスを継承することになります。アプリケーションでコントローラオブジェクトが必要な場合は、既製のコントローラクラス(NSObjectControllerなど)の1つを使用するか、または別の動作を必要とする場合はNSControllerまたはNSObjectをサブクラス化します。作成するクラスが典型的なモデルクラスである場合(たとえば、各オブジェクトがスプレッドシート内の会社データの各行を表すようなクラスの場合)は、おそらくNSObjectをサブクラス化するか、Core Dataフレームワークを使用することになります。
さらに、サブクラス化が問題解決のための最適な手段でないこともあります。より適切な手段が存在するかもしれません。少数の簡易メソッドをクラスに追加するだけなら、サブクラスの代わりにカテゴリを作成することが考えられます。あるいは、Cocoa開発“ツールボックス”に含まれる、デザインパターンをベースとしたほかの数多くのリソースの1つを利用することもできます。このようなリソースには、委任、通知、ターゲット/アクションなどがあります(“「オブジェクトとの通信」”を参照)。スーパークラスの候補を決める際には、クラスのヘッダファイル(またはリファレンスドキュメント)をひととおり調べ、委任メソッドや通知など、サブクラス化をせずに必要な動作を実行できる何らかのメカニズムがないか確認します。
フレームワークのプロトコルについても同様に、そのヘッダファイルやドキュメントを調べることができます。プロトコルを採用することによって、複雑なサブクラスに伴う困難を避けながら目標を達成できる場合があります。たとえば、メニュー項目の有効状態を管理する必要がある場合は、カスタムコントローラクラスでNSMenuValidationプロトコルを採用することができます。この動作を実現するためにNSMenuItemやNSMenuをサブクラス化する必要はありません。
オーバーライドを前提としていないフレームワークメソッドが存在するのと同様に、サブクラス化を前提としていないフレームワーククラスも存在します(NSFileManager、NSFontPanel、NSLayoutManagerなど)。これらのクラスをあえてサブクラス化する場合は、十分な注意が必要です。フレームワーククラスによってはその実装が複雑なものがあり、ほかのクラスの実装と密接に統合されているものや、場合によってはオペレーティングシステムのさまざまな要素と密接に統合されているものがあります。多くの場合、フレームワークメソッドの動作を正確に複製したり、メソッドが持つと考えられる相互依存性や影響を推定することは、困難です。何らかのメソッド実装に加えた変更のために、予見不能な望ましくない影響が広範囲に生じる可能性もあります。
場合によっては、オブジェクト構成を使用してこのような困難を避けることもできます。オブジェクト構成とは、オブジェクトを“ホスト”オブジェクトに集め、ホストを通じてそれらのオブジェクトを管理し、カスタマイズした高度で複雑な動作を実現するという、汎用的な手法です(Figure 3-3を参照)。フレームワークの複雑なスーパークラスから直接継承するのではなく、そのスーパークラスのインスタンスをインスタンス変数として保持するカスタムクラスを作成します。カスタムクラスそのものは、ルートクラスであるNSObjectを直接継承するなど、かなり単純にできる可能性があります。継承という点では単純ですが、クラスでは埋め込まれたインスタンスを操作し、拡張し、強化します。クライアントオブジェクトから見れば、ある意味では、対象の複雑なスーパークラスのサブクラスと捉えることができますが、スーパークラスのインターフェイスを共有することはおそらくありません。オブジェクト構成の例として、Foundationフレームワーククラスの NSAttributedStringがあります。NSAttributedStringはNSStringオブジェクトをインスタンス変数として保持しており、stringメソッドを通じてそれを公開しています。 NSStringは、文字列のエンコードや、文字列の検索、パス操作などの複雑な動作を実行するクラスです。NSAttributedStringはこれらの動作を強化し、フォントや色、位置揃え、段落スタイルなどの属性を一連の文字に付加する機能を追加します。そして、この機能追加を、NSStringをサブクラス化することなく実現します。
一見、スーパークラスの最適な候補と思われるものが、最適な選択でないこともあります。ご存知のように、NSViewオブジェクトはCocoaのほとんどの描画で使用されているオブジェクトです。しかし、描画プログラムやCADプログラムなど、数百から数千のグラフィック要素を扱う可能性のあるプログラムを設計する場合は、NSViewを継承したものではない、独自のカスタムグラフィック要素クラスを設計して使用することを検討します。オブジェクトのサイズという点から見た場合、NSViewオブジェクトには多数のインスタンスデータが付随します。独自に作成したカスタムクラスのグラフィック要素インスタンスを使用したほうが、“軽量”でありながら、単独のNSViewオブジェクトでそれらの要素を描画するのに必要となるすべての情報を保持できる可能性があります。
| < Previous PageNext Page > |
Last updated: 2006-05-23
|
Get information on Apple products.
Visit the Apple Store online or at retail locations. 1-800-MY-APPLE Copyright © 2007 Apple Inc. All rights reserved. | Terms of use | Privacy Notice |