世界の中心でコンポーネントを叫ぶ
キャンバスに表示したい図形要素(コンポーネント)は、他のアブリケーションでも再利用します。そこで、クラスGeometry
の傘下に、種々の部品群を規定します。
class Geometry: drawColor = Color.black fillColor = Color.white def __init__(self, drawColor, fillColor=None): if drawColor: self.drawColor = drawColor self.fillColor = fillColor def transfer(self, context, p): ox, oy, kx, ky = context x, y = p return ox+int(kx*x), oy-int(ky*y)
クラスGeometry
は、すべての幾何図形に共通する特性を規定します。
class Circle(Geometry): def __init__(self, center, radius, drawColor=None, fillColor=None): Geometry.__init__(self, drawColor, fillColor) self.center = center self.radius = radius def paint(self, g): x, y = self.center r = self.radius cx, cy, d = x-r, y-r, 2*r if self.fillColor: g.color = self.fillColor g.fillOval(cx, cy, d, d) g.color = self.drawColor g.drawOval(cx, cy, d, d) def transform(self, context): ox, oy, kx, ky = context return Circle( (self.transfer(context, self.center)), ky*self.radius, self.drawColor, self.fillColor)
クラスCircle
は、円に固有の特性を規定します。
class Dot(Circle): def transform(self, context): return Circle( (self.transfer(context, self.center)), self.radius, self.drawColor, self.fillColor)
クラスDot
は、点(小円)に固有の特性を規定します。
class Line(Geometry): def __init__(self, start, end, drawColor=None): Geometry.__init__(self, drawColor) self.start = start self.end = end def paint(self, g): g.color = self.drawColor x1, y1 = self.start x2, y2 = self.end g.drawLine(x1, y1, x2, y2) def transform(self, context): start = self.transfer(context, self.start) end = self.transfer(context, self.end) return Line(start, end, self.drawColor)
クラスLine
は、直線に固有の特性を規定します。
class Polyline(Geometry): def __init__(self, points, drawColor=None): Geometry.__init__(self, drawColor) self.points = points def paint(self, g): g.color = self.drawColor g.drawPolyline( [x for x, y in self.points], [y for x, y in self.points], len(self.points)) def transform(self, context): s = [] for e in self.points: s.append(self.transfer(context, e)) return Polyline(s, self.drawColor)
クラスPolyline
は、折れ線に固有の特性を規定します。
世界の中心でビューを叫ぶ
アブリケーションを実行する準備が整いました。ここまで作成したコードの断片は、クラス単位で再利用できます。以下のコードを実行すると、その動作を確認できます。
def paint(g): CanvasPanel.paint(panel, g) container.paint(g) panel = CanvasPanel() panel.paint = paint step, seconds = 60, 0.05 container = Container() radius, R = 10, 3 cycloid = Cycloid(radius, 2*pi, step) xpoints = cycloid.xpoints() context = 50, 70, 3, 3 def animate(e): for i in range(step+1): sleep(seconds) container.clear() e = Line((xpoints[i], radius), cycloid[i]) container.add(e.transform(context)) e = Circle((xpoints[i], radius), radius, Color.blue) container.add(e.transform(context)) e = Polyline(cycloid[:i+1], Color.red) container.add(e.transform(context)) e = Dot(cycloid[i], R, Color.red, Color.yellow) container.add(e.transform(context)) panel.paintImmediately(bounds) button = JPanel(layout=GridLayout(1, 0)) button.add(JButton("animate", actionPerformed=animate)) bounds = Rectangle(0, 0, 360, 100) frame = JFrame(defaultCloseOperation=JFrame.EXIT_ON_CLOSE, bounds=bounds, title="Cycloid", layout=BorderLayout()) frame.add(panel, BorderLayout.CENTER) frame.add(button, BorderLayout.WEST) frame.visible = True
関数animate
では、ボタン[animate]を押したときに、panel
内に描かれる図形を規定します。サイクロイドを描くときに、円周上の定点が描く軌跡をアニメーション効果を使って表現します。
関数animate
の役割は、Cycloid
/Container
/Geometry
を仲介することです。Cycloid
は、専門領域の知識の他に、アブリケーションに依存する情報を保持しています。Container
は、任意の図形Geometry
を追加(add
)した後で、ウィンドウ内に表示(paint
)します。Geometry
は、Line
(直線)、Circle
(円)、Polyline
(折れ線)、Dot
(点)に共通する特性を規定します。
ここで注目して欲しいのは、Cycloid
は、専門領域の知識とアブリケーションに依存する情報とを、分割統治する必要があることです。専門領域の知識がない開発者には、アブリケーションを開発するのに必要な情報だけを提示します。なぜなら、開発者は、その数学的な背景を知らなくても、任意の図形をいつどこに表示するかだけを理解できればいいからです。こうして、専門家と開発者とは、相手の専門領域(自分には未知の領域)に立ち入らずに、分業化が可能になります。
このようにして、サイクロイドという専門領域の問題も、アブリケーション開発者には、単にウィンドウ内に図形を描くという一般領域の問題へと帰着します。
世界の中心で何を叫ぶ?
アジァイル開発の現場では、分業化が進んでいます。分析チーム、設計チームなど、親請け下請けという従来型の上下関係ではなく、分業化された専門家集団(マトリックス型組織)が形成されます。プログラミングチームも、コンポーネント開発グルーブ、モデル開発グルーブのように分割統治が徹底されています。そのようなグループ間では「コンポーネントはまだか」「モデルはどうした」という叫びが聞こえてきます。