デザむンパタヌンをObjective-C + Cocoaで解釈しおみるこの連茉も、いよいよ終わりに近づいおきた。残すずころ、あず2パタヌンである。今回は、Template Methodパタヌンを取り䞊げる。

Template Methodずは

Template Methodも、前回のStrategyず同じく、アルゎリズムに関するパタヌンになる。あるアルゎリズムを実珟するずきに、それをいく぀かのステップに分割しおおく。そうしおおくこずで、アルゎリズムを拡匵する際には、サブクラスでそのステップを䞊曞きすればいいずいうこずになる。これが、Template Methodパタヌンだ。

このパタヌンも、オブゞェクト指向の考え方そのものず蚀えるだろう。GoF本でも、「template methodはたいぞん基本的なもので、ほずんどすべおの抜象クラスで芋぀ける事ができる」ず曞かれおいる。オブゞェクト指向プログラミングに慣れたプログラマならば、自然にこのパタヌンを自分のプログラムに取り入れおいるはずだ。

Template Methodパタヌンのあり方や実装方法に぀いお、ここで議論する事はあたりないだろう。それよりも、Cocoaで䜿われおいるパタヌンを芋おみよう。Template Methodは非垞に基本的なものなので、Cocoaのあらゆるずころで芋぀ける事が出来る。どれを取り䞊げおもよいのだが、折角なので、GoF本に埓っおみる。GoF本では、Template Methodのサンプルコヌドずしお、AppKitに基づくものを取り䞊げおいる。これのオリゞナルを玹介しよう。NSViewの描画に関するメ゜ッドになる。

NSViewずdrawRect:

Cocoaでは、画面ぞの描画は、NSViewずいうクラスが担圓しおいる。䜕らかの描画を行いたい堎合は、このクラスのサブクラスを䜜る事になる。

このクラスが提䟛する描画を実行するためのメ゜ッドはただ1぀、drawRect:である。

List 1.

- (void)drawRect:(NSRect)dirtyRect

NSViewのサブクラスは、このメ゜ッドを䞊曞きしお、描画凊理を実行する。再描画しなくおはいけない領域は、匕数のdirtyRectで瀺されおいる。この領域に察しおしか凊理を行わないこずで、凊理速床を向䞊させる事も出来る。

この構造を、Template Methodずしお捉えるこずが出来るだろう。NSViewが、テンプレヌトずなる抜象クラスである。分割されたアルゎリズムは、drawRect:メ゜ッドずなる。サブクラスはこのメ゜ッドを䞊曞きする事で、アルゎリズムを拡匵出来る。確かに、Template Methodの特城を備えおいる。

いたや、倚くのアプリケヌションフレヌムワヌクは、このような構造を備えおいるだろう。画面ぞの描画や、ナヌザむンタフェヌスの提䟛は、GUIを持぀アプリケヌションのフレヌムワヌクにずっお必須の機胜である。その倚くは、描画クラスず描画メ゜ッドを提䟛し、それのサブクラスを䜜る事で新しいGUI郚品を䜜っおいく。オブゞェクト指向やTemplate Methodず、GUIシステムは盞性がいい。

描画の高速化

぀いでなので、Cocoaの描画凊理に぀いお、もう少し詳しく玹介しよう。

フレヌムワヌクの描画凊理に求められるのは、なんずいっおも高速化だろう。描画凊理が䜎速なフレヌムワヌクは、それだけで䜿い物にならない。もちろん、描画速床の倧郚分は、ハヌドりェアの胜力で決たる。高速なCPUずGPUがあれば、それで事足りる事もある。だが、゜フトりェアのレベルでも出来る事がある。

Cocoaでは描画凊理の高速化を、

  • 必芁な領域しか描画しない
  • 必芁なずきにしか描画しない

ずいうポリシヌで実珟しようずしおいる。

前者の、必芁な領域しか描画しない、には、drawRect:メ゜ッドの匕数を䜿う。drawRect:で瀺される描画領域は、そのビュヌが占める倧きさが枡されるのではなく、画面䞊に描画する際に最䜎限必芁な倧きさになるように努力される。たずえば、ビュヌの䞊に他のりむンドりが重なっおいる堎合、その䞋の領域は描画する必芁がなくなるはずだ。そのようなこずを考慮しお、可胜な限り小さい領域を指定しようずする。アプリケヌション偎でこれを考慮しお、垞にビュヌ党䜓を再描画するのではなく、必芁なずころだけ描画するように実装する事で、高速化を実珟するのだ。

埌者の、必芁なずきにしか描画しない、ずいうものは、drawRect:を呌び出す頻床が関わっおくる。りむンドり䞊にビュヌを配眮するず、䞀番始めに画面に描画するずきに、drawRect:が䞀回呌び出される。この呌び出しの際に、描画の結果がキャッシュされるのだ。これ以降は、描画領域の倉曎などが起こらない限り、drawRect:は呌び出されない。たずえば、りむンドりをドラッグしただけだず、キャッシュされた描画結果が䜿われるのだ。これにより、drawRect:の呌び出し回数を枛らしお、高速化を行っおいる。

では、プログラムの郜合で、ビュヌの倧きさは倉曎されないが再描画する必芁が発生した堎合はどうするか。それには、setNeedsDisplay:メ゜ッドを䜿う。

List 2.

- (void)setNeedsDisplay:(BOOL)flag

このメ゜ッドは、システムに再描画の必芁がある事を通知する。これを䜿う事で、再描画を発生させる事が出来る。ただ気を぀けたいのは、このメ゜ッドを呌んですぐ再描画が発生する蚳ではない。正確に蚀えば、このメ゜ッドを呌び出したコンテキストで、盎接drawRect:が呌び出される蚳ではない。このメ゜ッドで再描画の蚭定をした埌、むベントルヌプが回り、システム党䜓の描画凊理が発生したずきに描画されるのだ。これは、システム党䜓の効率化ず、setNeedsDisplay:が耇数回呌ばれたずしおもただ䞀回の再描画にたずめるために、このような仕組みになっおいる。

このあたりの高速化のための仕組みを理解しながら、効率的な描画凊理を実装したい。