次のイテレーションで、依頼主から、このアプリケーションで今度はOracleをサポートしなければならないと指示されたとします。
残念ながら、この2つの異なるデータベースシステムには、日付について別々の書式が必要です。互換性とはまた厄介な問題です。私ははるか昔、「現実の」プロジェクトにおいて、実際に似たようなことをしなければならなかった経験があります。そのときは、2つの別々の環境に導入を行う必要がありました。
心配はいりません。単体テストを使用して、ソリューションの構築と問題回避に役立てていきましょう。ただし、まずは問題に対する最良の解法を調査し、手短に検討することにします。
Oracleでは日付の書式に少なくとも2つの方式があり、いずれもMySQLのものとは異なります。簡単にWebで検索したところ、Oracle SQLで日付を与える方法の1つは、デフォルトの形式である「DD-MON-YY」を前提にすることです。たとえば、挿入文字列は「30-may-2007」となります。
もう1つの方法は、日付文字列の書式を指定するTO_DATE関数を提供することです。
TO_DATE('2007-07-15', 'yyyy/mm/dd')
私はこちらの方が好みです。この方法であれば、「07-may-06」が2007年5月6日なのか2006年5月7日なのか、混乱せずにすみます。
考えられる解決策としては、insertString内にif文を組み込むという方法があります。たぶんこのやり方では、コードの他の多くの箇所、おそらくselect文字列の生成用途でif文が必要となるでしょう。これはあまり良くありません。if文を無節操に使ったコードは、すぐに手に負えなくなります。
それよりも、DBMSの種類を特定するための場所を1つ用意した方がよいでしょう。その場所で、さらにDBMS個別のオブジェクトを用意し、それぞれの書式に沿った日付文字列を生成させるようにすることもできます。
ここで私は、変更された振る舞いを扱うためにDateColumnを変更することに少し不安を感じます。この例に関して言うと、Strategyデザインパターンの全体の考え方は、DBMS個別のフォーマッタオブジェクトをDateColumnオブジェクトに渡すというものです。このフォーマッタオブジェクトのクラスには、目的に合った振る舞いが含まれます。
実装の観点から、単一のformatメソッドを宣言するDateFormatterインターフェイスを作りたいと思います。さらに、このインターフェイスを実装するMySQLDateFormatterとOracleDateFormatterを作成します。その後、DateFormatterの参照を維持するためにDateColumnクラスを変更することにします。
これでだいたいの道筋はつきました。目標地点まで少しずつ進んでいくことにします。最初に取り組むのは、MySQLDateFormatterに委譲するためにDateColumnを変更することです。次のように変更します。
// DateColumn.java ... public class DateColumn { ... public String insertString(Date date) { return new MySQLDateFormatter().format(date); } } // MySQLDateFormatter.java import java.util.*; public class MySQLDateFormatter { public String format(Date date) { return String.format("'%1$tY-%1$tm-%1$td'", date); } }
その後、テストを実行します。また、DateColumnのテストコードをコピーして修正することで、新規クラス用のテストを新たに作成します。
何はともあれ、新規クラスを作成する際にテストクラスを追加するのは、よい考え方です。これらの新たなテストクラスは、全部のクラスを単体テストで確認し実証する必要があることを他の開発者に対して穏便に示す、リマインダとしての働きをします。
import static org.junit.Assert.assertEquals; import java.util.*; import org.junit.*; public class MySQLDateFormatterTest { @Test public void insert() { MySQLDateFormatter formatter = new MySQLDateFormatter(); assertEquals("'2007-07-15'", formatter.format(DateUtil.createDate(2007, Calendar.JULY, 15))); } }
それから、Oracleに対応する部分を作成します。
// OracleDateFormatterTest.java: import static org.junit.Assert.assertEquals; import java.util.*; import org.junit.*; public class OracleDateFormatterTest { @Test public void insert() { OracleDateFormatter formatter = new OracleDateFormatter(); assertEquals("TO_DATE('2007/07/15', 'yyyy/mm/dd')", formatter.format(DateUtil.createDate(2007, Calendar.JULY, 15))); } } // OracleDateFormatter.java: import java.util.*; public class OracleDateFormatter { public String format(Date date) { return String.format("TO_DATE('%1$tY/%1$tm/%1$td', 'yyyy/mm/dd')", date); } }
