1a)対象となる要素:Element

Visitor::Element では、対象とする要素に共通するプロトコルを規定します。

## --------------------             # Visitor::Element
class XShape:
    def accept(self, v):
        source = "v.visit_%s(self)"%self.__class__.__name__
        eval(source)    ; print source

多種多様な対象を扱いたいときに、それらの違いを捨象するとともに、動的な問題解決が可能になるメカニズムが提供されていると、便利です。すると、XShape の傘下では、個別にメソッド accept を用意する必要がなくなります。次のような(単調な)コードを何度も繰り返し記述することを想像すると、その効用が分かります。

## --------------------
class EllipseShape:                 # ('.')
    def accept(self, v):
        v.visit_EllipseShape(self)
class PolygonShape:                 # (-.-)
    def accept(self, v):
        v.visit_PolygonShape(self)
class BooShape:                     # (=.=)
    def accept(self, v):
        v.visit_BooShape(self)

どのメソッドを必要とするかは、実行時にオブジェクト自身 self に決定させるのが得策です。「クラス」指向から「オブジェクト」指向への扉を開く鍵は、こんなところにも落ちています。


《Tips》 未熟な OOP 言語仕様のツケをパターンで清算する憂き目(力仕事:ハードコーディング)から、プログラマーを解放します。単調な作業の繰り返しは、得てして後で見つけるのが困難なバグを誘発しがちです。ハードウェア/ソフトウェアの特性を知ることで、プログラム(算譜)の質の向上が期待できるのと同様に、ヘッドウェアの特性を知っておくと、プログラミング(作譜)の質の向上が期待できます。□

1b)対象となる要素:ConcreteElement

Visitor::ConcreteElement では、Visitor::Element で規定されたプロトコルに従いながら、対象とする要素に固有の特性を実現します。

## --------------------             # Visitor::ConcreteElement
class EllipseShape(XShape):
    def __init__(self):
        self.shape = Ellipse(
            Stroke=Brushes.Blue,
            StrokeThickness=2,
            Width=100,
            Height=50,
            )

対象とする要素のひとつが、ここで取り上げた Ellipse です。

## --------------------             # Visitor::ConcreteElement
class PolygonShape(XShape):
    def __init__(self):
        points = PointCollection()
        vertices = "0,28 80,28 12,80 40,0 68,80"
        for e in vertices.split(" "):
            x, y = eval(e)
            points.Add(Point(x, y))
        self.shape = Polygon(
            Stroke=Brushes.Blue,
            StrokeThickness=2,
            Points=points,
            )

対象とする要素のひとつが、ここで取り上げた Polygon です。


《Tips》 この事例では、ひとつのモジュールを扱っていますが、実際の開発では、個別にモジュールを用意します。要求仕様の変更に伴い、新たな対象を扱いたいときには(Open)モジュールを追加するだけです。このとき(Closed)既存のモジュールはその影響を受けません。すると、開放閉鎖原則(OCL: Open-Closed Principle)に沿ったアプローチが可能になります。□


Previous|1/3|Next

適用事例: Visitor パターン

多種多様な対象を扱うとともに動的な問題解決を図りたいとき、すぐに思い浮かぶのは古典的な〈GoF〉Visitor パターンです。そこで、前述した事例に、このパターンを適用してみます。すると、要求仕様の変更に伴って新たな対象を取り込みたいときに、既存のリソースに影響を与えることなく、その対象ごとに最適な表現手段(選択肢)を独立して扱えるようになります。
まず、パターンの復習から始めます。古典的な Visitor パターンを踏襲したのが、VisitorBrush.py です。



>ipy.exe VisitorBrush.py

このアプリケーションを起動すると、ウィンドウが現れます。1)任意のタブをクリックすると、2)選択したブラシ使って描画した図形が左側に表示されます。右側の図形は、外枠だけが表示されます。これは、将来の機能変更に伴って、新たな表現手段が可能になったときに、その動作を確認できる余地を残したもので、Visitor パターンの顕著な効用のひとつです。