PR

 設計にはそれぞれ得失があり,一面だけを見て,ある設計が優れていると断ずることは危険です。そこで以下では代替案を挙げ,メリットやデメリットを比較検討してみます。

タイプセーフでないとコンパイル時に検出できず

 問題1は,言ってみればコーディング時のタイプミスが原因です。とはいえコンパイラでチェックできず,実行時エラーになりますので,放っておけない問題です。すぐに思い付く代替案は,定数定義でしょう(List3)。

public class ItemColumn {
 private ItemColumn() {}
 public static final String ITEM_ID = "itemid";
 public static final String ITEM_NAME = "itemname";
 public static final String UNIT_PRICE = "unitprice";
}
List3●定数定義を用いた例

 List3のように列名を定義し,「“itemid”」といったように文字列を指定する代わりに,「ITEM_ID」という定数を指定します。「ITEM_ID」を指定する部分でタイプミスをして「ITEMID」としてしまうと,コンパイル・エラーとなり,間違いに気付くことができます。しかし,この定数の使用を強制する術はありません。また,全く無関係の列を指定してしまったとしても,それをコンパイル時に検出できません。このような特性を「タイプセーフでない」と言います。

 では,「タイプセーフ」にするにはどうすればいいのでしょうか。その一つにタイプセーフである列挙型の「Enum」を用いる方法があります(List4)。Enumは,一つの型を生成します。DAOの(データベースからの読み込み)コードについてはここでは省略しますが,「Enum.valueOf()」を使用すると文字列からEnumに変換できますので,これを利用して列名から対応するEnumに変換します(図2)。アプリケーション側はJava5から導入された「ジェネリクス」という機能を使用し,Mapのキー(列名)を指定するのにEnumを使用します。Enumを用いると,列名のタイプミスの間違いは,ほぼすべてコンパイル時に検出できます。この方法のデメリットは,いちいちEnumの定義を用意しなければならないという点です。

public enum ItemColumn {
 ITEMID, ITEMNAME, UNITPRICE
}
List4●列挙型Enumを用いた例
図2●Enumを用いた場合のDAO
図2●Enumを用いた場合のDAO
[画像のクリックで拡大表示]

キャストをなくすには「行クラス」を定義

 では次に,問題2を回避する代替案を説明します。図1で説明したようにHashMapではObject型でデータを格納するので,取り出すときにキャストが必要になり,そのキャストで間違うと「ClassCastException」が起きます。コンパイラではチェックできず,実行時のエラーになります。これを回避するには,Object型でデータを保持せず,String型なりInteger型なりの型でデータを保持することです。図1のRDBMS のケースでは,List5のようなItemRowクラス(以下,行クラス)を定義します。この場合のアプリケーションのコードは,List6のようになります。

public class ItemRow {
 private String itemid;
 public String getItemid() {
  return itemid;
 }
 private String itemname;
 public String getItemName() {
  return itemname;
 }
 private long unitprice;
 public long getUnitPrice() {
  return unitprice;
 }
}
List5●行クラスの例
List result = dao.query();
for (ItemRow row:result) {
 String itemName = row.getItemName();
 System.err.println(itemName);
}
List6●行クラスを用いた場合のアプリケーションのコード例

 行クラスを用いるとDAOのコードも修正が必要です。データベースからのデータの読み込みは,ここでは省略しますが,列名を基に「リフレクション」(文字列で型やフィールド,メソッドにアクセスするJavaの仕組み)を利用し,列から行クラスのオブジェクトに値を設定します。例えば,RDBMSの列名を単純に小文字にしたものを行クラスのフィールド名とします。リフレクションを利用しているため,フィールドはList5のように「private」を指定しても構いません。この方法であれば列名を間違える心配が無いだけでなく,取得した値の型も間違えることがありません。

 また,IDE(統合開発環境)を利用している場合,「String itemName = row.」まで入力すれば,IDEが入力候補を表示します。アプリケーション開発者は行クラスを正確に記憶していなくても,IDEによってコーディングが支援されます。

 デメリットとしては,いちいち行クラスを作成しなければならない点が挙げられます(これはタイプセーフEnumよりも少しだけ手間がかかります)。もっとも内容は単純なので,IDEによる自動生成が可能でしょう。また,リフレクションを使用していることから,データの読み込みに当たってはパフォーマンスに若干のオーバーヘッドがある点も挙げられます。しかし大抵はDBアクセスの方が圧倒的に遅いため,通常はここが問題になることはほとんどありません。アプリケーション内でデータにアクセスする際には,HashMapよりも行クラスのオブジェクトの方が高速にアクセスできます。

宇野 るいも(本名:花井 志生/はない しせい)
日本IBM グローバル・ビジネス・サービス ソフトウェア・エンジニアリング ITスペシャリスト
1991年日本IBM入社。アセンブラ/C/C++を中心とした,組み込みシステムの開発を担当。現在はJava EEを利用したシステム開発における,システム設計および実装作業,トラブル時の火消しとして,インスペクションやチューニング作業を中心に担当。「Strutsプログラミング講座」(アスキー)など,主にJava EEに関連した書籍の執筆多数