両方のパターンを揃えたところで、DateColumnのテストコードを変更してフォーマッタの利用法を指定します。
import static org.junit.Assert.*; import java.util.*; import org.junit.*; public class DateColumnTest { private static final String NAME = "name"; private DateColumn column; ''private DateFormatter formatter;'' @Before public void initialize() { ''formatter = new MySQLDateFormatter();'' ''column = new DateColumn(NAME, formatter);'' } @Test public void create() { assertEquals(NAME, column.getName()); } @Test public void insert() { ''Date date = DateUtil.createDate(2007, Calendar.JULY, 15);'' ''assertEquals(formatter.format(date),'' ''column.insertString(date));'' } }
このテストコードをコンパイルするには、DateFormatterインターフェイスが必要です。
import java.util.*; public interface DateFormatter { String format(Date date); }
MySQLDateFormatterクラスは、このインターフェイスを実装しなければいけません。
public class MySQLDateFormatter implements DateFormatter { ...
このテストを通すためには、DateColumnクラスに2、3簡単な修正が必要です。
import java.util.*; public class DateColumn { private String name; ''private DateFormatter formatter;'' public DateColumn(String name, ''DateFormatter formatter'') { this.name = name; ''this.formatter = formatter;'' } public String insertString(Date date) { return ''formatter''.format(date); } public String getName() { return name; } }
これで完了でしょうか? いえ、まだあります。
OracleDateFormatterが利用できることを確認する必要があります。DateColumnに追加のテストコードを書くか、または、手っ取り早くテストコードを次のように変更します。
@Before public void initialize() { formatter = new ''OracleDateFormatter();'' column = new DateColumn(NAME, formatter); }
この変更に伴い、OracleDateFormatterクラスがDateFormatterインターフェイスを実装するようにします。
public class OracleDateFormatter implements DateFormatter { ...
単体テストを行い、ここまでに実装したものをすべて確認します。DateColumnを使用するクライアントコードについては、ここでは割愛しています。このクライアントコードは、フォーマッタの正確な種類をDateColumnの各インスタンスに渡す役割を果たすものです。おそらくこのロジックはFactoryの一部であり、これに対しては簡単に単体テストを記述できるでしょう。
これで、Strategyパターンに関する作業は完了です。DateColumnクラスは、クライアントから値文字列の提供を求められると、その処理を適切なDateFormatterのインスタンスに委譲します。DateColumnのコードは、処理に使用しているDBMSの種類を意識しません。DateColumnにif文は必要ありません。DateColumnクラスは、少なくとも日付の書式設定ロジックの変更という観点においては、変更に対し「閉じられて」います。
まとめ
こつこつと段階的なステップで進めてきたおかげで、クールなパターンの実装にたどり着くことができました。私がこれを「クール」だと思うのは、パターンを構成するクラスのそれぞれが小さく、理解しやすいからです。また、テストを通じて実装を進めていくことに重点を置いた結果、全体が自然に流れて、かつ大変テストしやすいものとなった点も評価できます。
繰り返しになりますが、テスト駆動型開発(TDD)を通じた単体テストは、優れたデザインに沿ったソリューションを生み出すのに大いに役立ちます。
参考資料
- 『Design Patterns: Elements of Reusable Object-Oriented Software』 Erich Gamma・Richard Helm・Ralph Johnson・John Vlissides 著、Addison-Wesley Professional、1995年、 p315.
