《付録》VisitorBrush.py

# -*- coding: utf-8 -*-
#===============================================================================
#    Copyright (C) 2000-2008, 小泉ひよ子とタマゴ倶楽部
#
# History: WPF examples
#    2008/01/25, IronPython 1.1.1 (download)
#    2008/08/22, IronPython 1.1.2 (download)
#    2008/03/16, ver.2.0, WPF
#    2008/00/00, ver.2.1, IronPython 1.1.2 
#===============================================================================
from _ant import *
from System import *
from System.Windows import *
from System.Windows.Media import *
from System.Windows.Media.Imaging import *
from System.Windows.Shapes import *

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

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

## --------------------                         # 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,
            )

## --------------------                         # Visitor::Visitor
class XVisitor:
    def visit_EllipseShape(self, e):
        raise NotImplementedError("%s.visit_EllipseShape"
            %self.__class__.__name__)
    def visit_PolygonShape(self, e):
        raise NotImplementedError("%s.visit_PolygonShape"
            %self.__class__.__name__)

## --------------------                         # Visitor::ConcreteVisitor
class SolidColorVisitor(XVisitor):
    def visit_EllipseShape(self, e):
        e.shape.Fill = Brushes.Yellow
    def visit_PolygonShape(self, e):
        e.shape.Stroke = Brushes.Black

## --------------------                         # Visitor::ConcreteVisitor
class LinearGradientVisitor(XVisitor):
    def visit_EllipseShape(self, e):
        brush = LinearGradientBrush()
        for color, offset in [
            ("Yellow", 0.0),
            ("Green" , 1.0),
            ]: 
            brush.GradientStops.Add(GradientStop(
                Color=getattr(Colors, color),
                Offset=offset,
                ))
        e.shape.Fill = brush
    def visit_PolygonShape(self, e):
        e.shape.Stroke = Brushes.Black

## --------------------                         # Visitor::ConcreteVisitor
class RadialGradientVisitor(XVisitor):
    def visit_EllipseShape(self, e):
        brush = RadialGradientBrush()
        for color, offset in [
            ("Yellow", 0.0),
            ("Green" , 1.0),
            ]: 
            brush.GradientStops.Add(GradientStop(
                Color=getattr(Colors, color),
                Offset=offset,
                ))
        e.shape.Fill = brush
    def visit_PolygonShape(self, e):
        e.shape.Stroke = Brushes.Black

## --------------------                         # Visitor::ConcreteVisitor
class ImageVisitor(XVisitor):
    def visit_EllipseShape(self, e):
        brush = ImageBrush(
            ImageSource=BitmapImage(Uri("sunflower.jpg", UriKind.Relative)))
        e.shape.Fill = brush
    def visit_PolygonShape(self, e):
        e.shape.Stroke = Brushes.Black

## --------------------                         # Visitor::ConcreteVisitor
class DrawingVisitor(XVisitor):
    def visit_EllipseShape(self, e):
        brush = DrawingBrush(
            Viewport=Rect(0, 0, 0.1, 0.1),
            TileMode=TileMode.Tile,
            )
        sheet = GeometryDrawing(
            brush=Brushes.Yellow,
            pen=None,
            geometry=RectangleGeometry(Rect(0, 0, 50, 50)))
        geometry = GeometryGroup()
        for (px, py), rx, ry in (
            *1
        marble = GeometryDrawing(
            brush=Brushes.Blue,
            pen=None,
            geometry=geometry)
        drawing = DrawingGroup()
        for g in sheet, marble:
            drawing.Children.Add(g)
        brush.Drawing = drawing
        e.shape.Fill = brush
    def visit_PolygonShape(self, e):
        e.shape.Stroke = Brushes.Black

## --------------------                         # Visitor::ConcreteVisitor
class VisualVisitor(XVisitor):
    def __init__(self, visual):
        self.visual = visual
    def visit_EllipseShape(self, e):
        brush = VisualBrush()
        brush.Visual = self.visual
        e.shape.Fill = brush
    def visit_PolygonShape(self, e):
        e.shape.Stroke = Brushes.Black

## --------------------                         # Visitor::ObjectStructure
class ExWindow(Window):
    def __init__(self, Content=None, **args):
        self.InitializeComponent(Content)
        self.init()

    def InitializeComponent(self, Content):
        self.Content = LoadXaml(Content)
        
    def init(self):
        target = (
            "solidColorBrush",
            "linearGradientBrush",
            "radialGradientBrush",
            "imageBrush",
            "drawingBrush",
            "visualBrush",
            )
        self._Controls(target)

    def _Controls(self, target):
        controls = xaml_controls(self)
        for e in target:
            setattr(self, e, controls[e])
        visitors = (
            SolidColorVisitor(),
            LinearGradientVisitor(),
            RadialGradientVisitor(),
            ImageVisitor(),
            DrawingVisitor(),
            VisualVisitor(self.imageBrush),
            )
        for e, v in zip(target, visitors):
            items = getattr(self, e)
            for s in EllipseShape(), PolygonShape():
                s.accept(v)
                items.Children.Add(s.shape)

## --------------------               
if __name__ == "__main__":
    xaml = __file__.replace(".py",".xaml")
    win = ExWindow(
        Title=xaml,
        Width=280, Height=200,
        Content=xaml,
        )
    Application().Run(win)

## --------------------               

*1:10, 10), 10, 10), ((35, 35), 15, 15), ): geometry.Children.Add(EllipseGeometry( Center=Point(px, py), RadiusX=rx, RadiusY=ry,

《付録》VisitorBrush.xaml

<!--
#    Copyright (C) 2000-2008, (^.^) piyo-piyo Tamago-Club
#
# History: WPF examples
#    2008/01/25, IronPython 1.1.1 (download)
#    2008/08/22, IronPython 1.1.2 (download)
#    2008/03/16, ver.2.0, WPF
#    2008/00/00, ver.2.1, IronPython 1.1.2 
-->
<TabControl
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
  xmlns:x="http://schemas.microsoft.com/winfx/2006/sparkle">

  <!-- SolidColorBrush -->
  <TabItem   Header="SolidColorBrush">
    <WrapPanel Name="solidColorBrush" />
  </TabItem>

  <!-- LinearGradientBrush -->
  <TabItem   Header="LinearGradientBrush">
    <WrapPanel Name="linearGradientBrush" />
  </TabItem>

  <!-- RadialGradientBrush -->
  <TabItem   Header="RadialGradientBrush">
    <WrapPanel Name="radialGradientBrush" />
  </TabItem>

  <!-- ImageBrush -->
  <TabItem   Header="ImageBrush">
    <WrapPanel Name="imageBrush" />
  </TabItem>

  <!-- DrawingBrush -->
  <TabItem   Header="DrawingBrush">
    <WrapPanel Name="drawingBrush" />
  </TabItem>

  <!-- VisualBrush -->
  <TabItem   Header="VisualBrush">
    <WrapPanel Name="visualBrush" />
  </TabItem>
</TabControl>

Visitor パターンに加えて

古典的な Visitor パターンに捕われることなく、動的な問題解決を導入したのが、サンプルファイル VisitorBrush.py です。


具体的な WPF コントロール(TabControl/TabItem/WrapPanel)に関する情報は、マークアップを使って VisitorBrush.xaml ファイルの中に封じ込めてあるので、分離コードでは、それに依存しない部分だけを記述するように努めます。

## --------------------             # Visitor::ObjectStructure
class ExWindow(Window):
    def _Controls(self, target):
        controls = xaml_controls(self)
        for e in target:
            setattr(self, e, controls[e])
        visitors = (
            SolidColorVisitor(),
            LinearGradientVisitor(),
            RadialGradientVisitor(),
            ImageVisitor(),
            DrawingVisitor(),
            VisualVisitor(self.imageBrush),
            )
        for e, v in zip(target, visitors):
            items = getattr(self, e)
            for s in EllipseShape(), PolygonShape():
                s.accept(v)
                items.Children.Add(s.shape)

すべての問題解決を静的(コンパイル時)に図るのではなく、それを動的(実行時)に委ねられるので、些末な状況に煩わされることなく、本質的な問題解決に専念できます。対象となる要素(EllipseShape/PolygonShape)と、それを表現する手段(SolidColorVisitor/LinearGradientVisitor/RadialGradientVisitor/ImageVisitor/DrawingVisitor/VisualVisitor)に関する問題提起を、事前に用意するだけで、事後の問題解決は、当事者に委ねるのを基調とします。
必要なときに Iterator を生成して、その機能を要求する立場(what)では組み込みの制御構造(for 文)を利用するだけです。すると、その要求を実現する立場(how)とは独立した記述が可能になります。
このように、さまざまな観点から役割分担を促進することで、複雑なアプリケーション開発の負荷を明確に分散できるようになります。さらに、機能を実現する方法に変更があっても、これに柔軟に対処できるようになります。

まとめ

機能の要求(what)と実現(how)とを分割統治するのに、Visitor パターンを適用するとともに、それを実現するための手段として、動的な問題解決を導入する方法を紹介しました。
さらに、もう一歩踏み込んで、このアプローチを積極的に導入したいなら、Visitor クラスに頼らなくても、Visitor オブジェクトを実行時に生成して、それに処理を委ねることも可能です。その詳細については、Smalltalk を参考にすると良いでしょう。