|
これから、新しい描画アイテムとして楕円を追加するとします。機能を拡張するときは、リファクタリングのタイミングとして適しているので、楕円追加に先立ってリファクタリングしてみましょう。
|
-----> Creater.java <--------------------
import java.awt.Point;
public interface Creater {
// 入力開始
public void init(Point point);
// 入力補助(ラバーバンド)
public void aid(Point point);
// 入力確定(モデル登録)
public void create(Point point);
}
-----> Creater.java <--------------------
-----> RectCreater.java <--------------------
import java.awt.*;
import java.awt.geom.Rectangle2D;
public class RectCreater implements Creater {
private DrawPanel panel;
private DrawEntityStore store;
private Point start;
private Rectangle2D rect;
public RectCreater(DrawPanel panel, DrawEntityStore store) {
this.panel = panel;
this.store = store;
rect = new Rectangle2D.Double();
}
// 入力を開始する
public void init(Point point) {
start = point;
rect.setRect(start.getX(), start.getY(), 0, 0);
store.addRubberEntity(rect);
if (panel != null) panel.repaint();
}
// 入力補助(ラバーバンド)
public void aid(Point point) {
resetRectangle(rect, point);
if (panel != null) panel.repaint();
}
// 入力確定する(作成)
public void create(Point point) {
Rectangle2D newRect = new Rectangle2D.Double();
resetRectangle(newRect, point);
store.addEntity(newRect);
store.removeRubberEntity(rect);
if (panel != null) panel.repaint();
}
// 四角を調整する
protected void resetRectangle(Rectangle2D rectangle, Point point) {
if (start.x < point.x && start.y < point.y) {
rectangle.setFrame(start.x, start.y, point.x - start.x, point.y - start.y);
} else if (start.x < point.x && start.y > point.y) {
rectangle.setFrame(start.x, point.y, point.x - start.x, start.y - point.y);
} else if (start.x > point.x && start.y < point.y) {
rectangle.setFrame(point.x, start.y, start.x - point.x, point.y - start.y);
} else {
rectangle.setFrame(point.x, point.y, start.x - point.x, start.y - point.y);
}
}
}
-----> RectCreater.java <--------------------
-----> LineCreater.java <--------------------
import java.awt.Point;
import java.awt.geom.Line2D;
public class LineCreater implements Creater {
private DrawPanel panel;
private DrawEntityStore store;
private Point start;
private Line2D line;
public LineCreater(DrawPanel panel, DrawEntityStore store) {
this.panel = panel;
this.store = store;
line = new Line2D.Double();
}
// 入力開始
public void init(Point point) {
start = point;
line.setLine(start, start);
store.addRubberEntity(line);
if (panel != null) panel.repaint();
}
// 入力補助(ラバーバンド)
public void aid(Point point) {
line.setLine(start, point);
if (panel != null) panel.repaint();
}
// 入力確定(モデル登録)
public void create(Point point) {
Line2D newLine = new Line2D.Double(start, point);
store.addEntity(newLine);
store.removeRubberEntity(line);
if (panel != null) panel.repaint();
}
}
-----> LineCreater.java <--------------------
|
まずは、クラス図でツールの構造を、図形作成を行なう部分に注目して見てみましょう。
DrawAdapterクラスがマウス入力イベントを受け付け、それを変換してRectCreaterや
LineCreaterといったCreaterインタフェースを実現したクラスに伝達するようになっています。
これと言って難しいところはありません。
楕円もこれらのクラスと同じく、Createrインタフェースを実現して作成されることになると思われます。
しかし、コードを見ると少し気になるところがあります。何か匂いませんか?
RectCreaterクラスもLineCreaterクラスもなんだか似ていますね。
第一に、サブクラス同士に同じフィールドが見られます。第二にコンストラクタ,
init(), aid(), create()といった全ての振る舞いが、
似たような処理を同じ手順で実行しているのに気付くでしょう。
第一については、リファクタリングカタログから「フィールドの引き上げ(320)」を
適用しましょう。第二はまぎれもなく不吉な匂い「重複したコード」です。
「Template Methodの形成(345)」を適用しましょう。
問題点 |
不吉な匂い |
予定するリファクタリング |
1 |
|
フィールドの引き上げ(320) |
2 |
重複したコード |
Template Methodの形成(345) |
リファクタリングにとりかかる前に必要なことは、テストを作成することです。
RectCreaterTest.javaでは、形状の作成方法が異なる4つの領域(図参照)の、ラバーバンド、
登録する図形の作成をテストします。今回はLineCreaterクラスのテストは省略します。そし
て、全てのテストにパスすることを確認しましょう。それではリファクタリング開始です。
|
-----> RectCreaterTest.java <--------------------
import java.awt.Point;
import java.awt.geom.Rectangle2D;
import junit.framework.TestCase;
public class RectCreaterTest extends TestCase {
private DrawEntityStore store;
private RectCreater creater;
protected void setUp() throws Exception {
super.setUp();
store = new DrawEntityStore();
creater = new RectCreater(null, store);
}
// case1 : start.x < end.x && start.y < end.y
public void testRubberCase1() {
creater.init(new Point(20, 40));
creater.aid(new Point(30, 60));
Rectangle2D rect = (Rectangle2D)store.getRubberEntities().get(0);
Rectangle2D correctRect = new Rectangle2D.Double(20, 40, 10, 20);
assertEquals(correctRect, rect);
}
// case2 : start.x < end.x && start.y > end.y
public void testRubberCase2() {
creater.init(new Point(20, 40));
creater.aid(new Point(40, 20));
Rectangle2D rect = (Rectangle2D)store.getRubberEntities().get(0);
Rectangle2D correctRect = new Rectangle2D.Double(20, 20, 20, 20);
assertEquals(correctRect, rect);
}
// case3 : start.x > end.x && start.y < end.y
public void testRubberCase3() {
creater.init(new Point(20, 40));
creater.aid(new Point(10, 60));
Rectangle2D rect = (Rectangle2D)store.getRubberEntities().get(0);
Rectangle2D correctRect = new Rectangle2D.Double(10, 40, 10, 20);
assertEquals(correctRect, rect);
}
// case4 : start.x > end.x && start.y > end.y
public void testRubberCase4() {
creater.init(new Point(20, 40));
creater.aid(new Point(10, 10));
Rectangle2D rect = (Rectangle2D)store.getRubberEntities().get(0);
Rectangle2D correctRect = new Rectangle2D.Double(10, 10, 10, 30);
assertEquals(correctRect, rect);
}
// case1 : start.x < end.x && start.y < end.y
public void testCreateCase1() {
creater.init(new Point(20, 40));
creater.create(new Point(30, 60));
Rectangle2D rect = (Rectangle2D)store.getEntities().get(0);
Rectangle2D correctRect = new Rectangle2D.Double(20, 40, 10, 20);
assertEquals(correctRect, rect);
}
// case2 : start.x < end.x && start.y > end.y
public void testCreateCase2() {
creater.init(new Point(20, 40));
creater.create(new Point(40, 20));
Rectangle2D rect = (Rectangle2D)store.getEntities().get(0);
Rectangle2D correctRect = new Rectangle2D.Double(20, 20, 20, 20);
assertEquals(correctRect, rect);
}
// case3 : start.x > end.x && start.y < end.y
public void testCreateCase3() {
creater.init(new Point(20, 40));
creater.create(new Point(10, 60));
Rectangle2D rect = (Rectangle2D)store.getEntities().get(0);
Rectangle2D correctRect = new Rectangle2D.Double(10, 40, 10, 20);
assertEquals(correctRect, rect);
}
// case4 : start.x > end.x && start.y > end.y
public void testCreateCase4() {
creater.init(new Point(20, 40));
creater.create(new Point(10, 10));
Rectangle2D rect = (Rectangle2D)store.getEntities().get(0);
Rectangle2D correctRect = new Rectangle2D.Double(10, 10, 10, 30);
assertEquals(correctRect, rect);
}
}
-----> RectCreaterTest.java <--------------------
|
この記事への評価にご協力をお願いします。
|
|