今回からはCommandパターンを取り上げる。この辺りから、Cocoaとの比較が俄然面白くなってくる。とりあえず、GoF本におけるCommandパターンの説明から始めよう。

Commandパターンとは

Commandパターンは、名前の通りコマンドを表すパターンだ。この「コマンド」は、広い意味ではあるオブジェクトに対する「要求」ということだが、正味な話、グラフィックユーザインタフェースにおけるコマンドのことと理解してしまっていいだろう。つまり、メニューを選択したり、ボタンを押したりして、アプリケーションに命令を送るコマンドのことだ。

このコマンドをクラスを使って表してやろう、というのがCommandパターンだ。例えば、コピーを行うためのCopyCommandクラス、ペーストを行うためのPasteCommandクラスを作成する。

そして、GoF本のCommandパターンの特徴は、それぞれのCommandクラスが、コマンドの「実行」も行う事だ。そのために、CommandクラスにExecuteという関数がついている。このExecute関数の中で、コピーやペーストといった処理を行おう、というものになる。

このCommandパターンを使うと、グラフィカルユーザインタフェースでの処理の流れは次のようになる。まず、ユーザがメニューの選択や、ボタンのクリックを行う。すると、それぞれのメニューやボタンに応じて、CopyCommandクラスやPasteCommandクラスのインスタンスを作る。そして、Execute関数を呼び出して、コマンドを実行する。

このようにして、コマンドの呼び出し元と、コマンドの実行を分離するのがCommandパターンの狙いだ。

Commandパターンの登場人物

Commandパターンの登場人物は、大きく分けると3つになる。まず、コマンドを作成して呼び出すInvoker。コマンドそのものを表すCommand。そして、コマンドの実行対象となるReceiverだ。

具体的に説明すると、Invokerはユーザが操作するメニューやボタン、Commandはコピーやペーストを表すものになる。そして、新たに登場したReceiverだが、これはCommandのExecute関数の中で、実際に操作される対象になる。

例えば、コピーコマンドを考えよう。ドキュメントを扱っているアプリケーションならば、ドキュメントの中で選択されている項目をクリップボードにコピーすることになる。そこで、CopyCommandのExecuteでは、ドキュメントを表す Documentクラスに対して、選択している項目のコピーを命じることになる。このDocumentクラスが、コマンドの実行の対象という事で、Receiverになる。

また、ドキュメントを開くコマンドも考えてみよう。これを、OpenCommandというクラスで表す。ドキュメントを開くというコマンドは、まずDocumentクラスに関連する。そして、開いたドキュメントはアプリケーション全体で管理する必要があるだろう。そこで、Applicationクラスを登場させる。OpenCommandでは、DocumentクラスとApplicationクラスとがReceiverになるだろう。

このように、CommandパターンのExecute関数を実現するには、あらかじめReceiverを設定しておかないといけない。

Commandパターンの実装

では、このようなGoFスタイルのCommandパターンを、Objective-Cで実装してみよう。

ここでは、Invokerの役割はClientが行うものとする。Commandとして、OpenCommandとPasteCommandを用意する。そしてReceiverには、先に紹介したようなApplicationとDocumentを使おう。

これらの関連は、次の図のようになる。

なかなか大掛かりになってきた。Commandのクラスからは、Receiverのクラスへの参照があるところに注目してほしい。これは、最終的なコマンドの実行はApplicationやDocumentといったReceiverのクラスで行われるためだ。

では、これをObjective-Cで実装してみよう。

まず、ApplicationとDocumentを、次のように定義する。

List 1.

@interface Application : NSObject
{
    NSMutableArray* _documents;
}
- (void)addDocument:(Document*)document;
@end

@interface Document : NSObject
- (void)open;
- (void)paste;
@end

Applicationは、インスタンス変数としてDocumentの配列を保持する。そのために、addDocument:というメソッドを提供する。Documentの方では、openとpasteというメソッドをそれぞれ用意した。

次にCommand側だ。ルートクラスとしてCommandクラスを用意する。

List 2.

@interface Command : NSObject
- (void)execute;
@end

このクラスには、executeメソッドを用意する。ここで、コマンドの実行を行う訳だ。

これで準備が整った。中心となるOpenCommand、PasteCommandクラスを実装してみよう。まず、OpenCommandだ。

List 3.

@interface OpenCommand : Command
{
    Application*    _application;
}
- (id)initWithApplication:(Application*)application;
@end

@implementation OpenCommand
- (id)initWithApplication:(Application*)application
{
    self = [super init];
    if (!self) return nil;

    _application = application;

    return self;
}

- (void)execute
{
    Document*   document;
    document = [[Document alloc] init];
    [_application addDocument:document];
    [document open];
}
@end

OpenCommandは、Applicationクラスへの参照が必要になる。そこで、初期化メソッドでその参照を渡してやる。

executeメソッドでは、まずDocumentクラスのインスタンスを作る。そして、それをApplicationに登録して、openメソッドを呼んでやればいい。

次は、PasteCommandだ。

List 4.

@interface PasteCommand : Command
{
    Document*   _document;
}
- (id)initWithDocument:(Document*)document;
@end

@implementation PasteCommand
- (id)initWithDocument:(Document*)document
{
    self = [super init];
    if (!self) return nil;

    _document = document;

    return self;
}

- (void)execute
{
    [_document paste];
}
@end

PasteCommandには、Documentの参照が必要だ。これも初期化メソッドで渡している。そうしておけば、executeメソッドではDocumentのpasteを呼ぶだけだ。

これが、GoFスタイルでのCommandということになる。次回は、現役のアプリケーションフレームワークであるCocoaでは、どのようにコマンドを処理しているのか紹介しよう。

提供:毎日キャリアバン ク

毎日キャリアバンクではITエンジニア出身のキャリアコンサルタントで形成する IT専門のチームを編成し、キャリアに応じた専任コンサルタントがご相談を承り ます。キャリアチェンジから市場価値の可能性、ご収入などの相談から面接のア ドバイスまでお気軽にご相談ください。求人情報誌や転職情報サイトなどで一般 に公開されていないような「急募求人案件」も随時ご紹介が可能です。まずはご 登録ください!