はじめに
CJ2EEのDataAccessObjectパターンは、企業向けシステム開発で利用される非常に優れたデザインパターンです。これを利用することにより、柔軟なシステムを構築することが可能となります。有名なパターンなので、多くの方はこのパターンを使った設計/開発に携わった経験があるのではないかと思います。
しかし、DataAccessObjectパターンを使った開発は多くのクラスやインターフェイスを定義する必要があります。これは、DataAccessObjectパターンがAbstructFactoryパターンをベースとしているためです。クラスやインターフェイスの数が増えると開発コストだけでなく管理コストも増大し、開発規模が大きくなるほど影響が大きくなります。
本稿では、こうしたDataAccessObjectパターンのデメリットを回避するためのパターンを紹介します。
対象読者
- 企業システム開発に携わっている方
- CJ2EEおよびGoFを理解されている方
DataAccessObjectパターン
DataAccessObjectパターンでは、BusinessObjectからデリゲートされたデータアクセスの要求に対して、DataAccessObjectがデータアクセス処理を行いその結果を返します。DataAccessObjectはDBなどのデータアクセス処理を隠蔽(抽象化)するため、BusinessObjectはデータアクセス処理の方法を意識する必要がなくなります。また、開発工程ではスタブまたはMySQL、結合試験工程ではOracleを使うといったこともできるようになります。
ところが、このDataAccessObjectパターンの導入には一つの問題があります。
DataAccessObjectパターンは少なくとも1つのテーブルに対して1つのDataAccessObject(データアクセスオブジェクト)クラスを定義する必要があります。さらにBusinessObjectとDataAccessObjectの間の情報を交換するためにDataTransferObject(データ転送オブジェクト)クラスも定義しなければなりません。場合によってはビューやシーケンスに対しても定義しなければならないかもしれません。
これだけで相当な数のクラスを定義したことになりますが、実は、これで最低レベルの話です。AbstructFactoryパターンをベースにしているということはAbstructFactory(抽象工場)クラスを定義するということです。さらに、DataAccessObjectやDataTransferObjectの抽象化を図るためのインターフェイスをクラスとは別に定義する必要もあります。
DataAccessObjectパターンではこれだけのクラスやインターフェイスを開発しなければならないので、最近では自動生成ツールの採用が常識となっています。
しかし、自動生成で定義されたDataAccessObjectはパフォーマンスが悪いと言われています。これはDataAccessObject自体のパフォーマンスが悪いというよりもDataAccessObjectの使用方法が最適ではないために発生することが多いのですが、DataAccessObjectはデータアクセス処理を隠蔽しているので、開発者にDataAccessObjectの最適な使用法を示すことが難しいのです。
もちろん、DataAccessObjectパターンを導入すべきケースはあります。ただ、導入時にはパターンによるメリット・デメリットを洗い出し分析する必要があるのです。残念ながら、そうしたことをきちんと行ってから導入するというプロジェクトは多くありません。
データアクセス処理を分離するもう一つの方法
さて、こうしたDataAccessObjectパターンのデメリットを挙げたところで、それに変わる方法が無ければ問題解決にはなりません。
問題解決の糸口を探す前に、DataAccessObjectパターンのメリットをもう一度確認してみましょう。我々の注目しているDataAccessObjectパターンの最大のメリットは「業務処理とデータアクセス処理の分離」ではないでしょうか。
データアクセス処理を分離したいのであれば、DataAccessObjectパターンを使う必要はありません。次の例を見てください。
public class BusinessLogic { public void businesslogic(Request req) { …… …… empInfo = selectEmpInfo(id); …… } private EmpInfoDto selectEmpInfo(String id) { …… // 従業員情報テーブルからデータを検索 …… } }
何か、ごく当たり前のコードに見えませんか?
しかし、このコードはきちんと「データアクセス処理の分離」という責任を果たしています。つまり、わざわざEmpInfoAccessObject
のようなクラスを別に定義しなくても、データアクセス処理を分離することは可能なのです。
確かに、このよく知られたこの方法では分離の強制という点では不十分かもしれません。コーディングガイドに上記のコードのように分離するように促しても、開発者はついついサボってしまうかもしれません。しかし、そういったことはコードインスペクションで回避できるのです。
DataAccessMethodパターン
DataAccessObjectパターンを使わなくてもデータアクセス処理を分離できることは述べました。しかし、DataAccessObjectパターンにはもう一つのメリットがあります。それはデータアクセス処理を切り替えられることです。
上記のコードでは、データアクセス層が分離されているとは言っても、DBMSの変更が発生すればクラスも修正しなければなりません。
こういったことまで解決するには、privateメソッドとして分離したのではダメです。次のコードを見てください。
public class BusinessLogic { public void businesslogic(Request req) { …… …… empInfo = selectEmpInfo(id); …… } protected EmpInfoDto selectEmpInfo(String id) { …… // 従業員情報テーブルからデータを検索 …… } }
何が変わったのでしょうか。
実は、selectEmpInfo
のスコープが単にprotetedに変わっただけです。しかし、これによって、MySQLからOracleに変更された場合でも、BusinessLogic
を触ることなくselectEmpInfo
をオーバーライドするだけで対応が可能となるのです。
では、さらに一歩進んで修正してみましょう。
abstract public class BusinessLogic { public void businesslogic(Request req) { …… …… empInfo = selectEmpInfo(id); …… } abstract protected EmpInfoDto selectEmpInfo(String id); }
何が起こったのでしょうか?
selectEmpInfo
の中身であるデータアクセス処理の実装を消してしまったのです。具象メソッドを抽象メソッドにしてしまったのです。
では、データアクセス処理の実装はどこに行ってしまったのでしょうか。
public class BusinessLogicMySQL extends BusinessLogic { protected EmpInfoDto selectEmpInfo(String id) { …… // 従業員情報テーブルからデータを検索 …… } }
消えたデータアクセス処理の実装は、サブクラスとして再定義されていたわけです。BusinessLogic
を継承したBusinessLogicMySQL
のselectEmpInfo
として復帰しています。
これはGoFのTemplateMethodパターンを応用したコードで、selectEmpInfo
をhookメソッドと言います。hookメソッドでデータアクセス処理を行うようにコーディングすることで、DBMSが変更されても柔軟に対応することができます。
この設計方式を、筆者はDataAccessMethodパターンと呼んでいます。