世界の中心でコンポーネントを叫ぶ
キャンバスに表示したい図形要素(コンポーネント)は、他のアブリケーションでも再利用します。そこで、クラス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は、専門領域の知識とアブリケーションに依存する情報とを、分割統治する必要があることです。専門領域の知識がない開発者には、アブリケーションを開発するのに必要な情報だけを提示します。なぜなら、開発者は、その数学的な背景を知らなくても、任意の図形をいつどこに表示するかだけを理解できればいいからです。こうして、専門家と開発者とは、相手の専門領域(自分には未知の領域)に立ち入らずに、分業化が可能になります。
このようにして、サイクロイドという専門領域の問題も、アブリケーション開発者には、単にウィンドウ内に図形を描くという一般領域の問題へと帰着します。
世界の中心で何を叫ぶ?
アジァイル開発の現場では、分業化が進んでいます。分析チーム、設計チームなど、親請け下請けという従来型の上下関係ではなく、分業化された専門家集団(マトリックス型組織)が形成されます。プログラミングチームも、コンポーネント開発グルーブ、モデル開発グルーブのように分割統治が徹底されています。そのようなグループ間では「コンポーネントはまだか」「モデルはどうした」という叫びが聞こえてきます。
