Nagise氏の「ぶっちゃけるとT.class やnew Tしたいケースは設計が悪いのだと思う。」という言葉の通りなのか、
Javaのジェネリクスで,T.class や new T() ができず悩んだ話 (型パラメータのインスタンス化に関し、フレームワーク設計からケーススタディ) - 主に言語とシステム開発に関して
の記事で取り上げられているフレームワークの設計には、おかしなところがある。
当記事には
その共通の性質は,BaseEntityに記述される。
とあるが、この点がおかしい。
この共通の性質というのは実はデータベースに関わる物理情報を管理するものである。
通常の DAO(Data Access Object)パターンでは、物理情報はDAOオブジェクトが管理する。
しかし、問題のフレームワークでは、データベースに関わる物理情報が
ビジネスオブジェクトであるエンティティクラスで管理されている。
(エンティティクラス http://code.google.com/p/.../Friend.java の logicalFromPhysical, toPhysicalEntity, tableName(), columns() などのメソッドがそれである。logicalFromPhysical をやるのが DAOオブジェクトではないか)
そこを直せば、以下のような素直な実装で、(裏技的に型引数を取得することなく、)要件が満たせるはず。
このサンプルコードは簡略化しているが大体のロジックは掴めると思う。
// Main.java import java.util.ArrayList; import java.util.List; public class Main { public static void main(String[] args) { FriendDAO fd = new FriendDAO(); Friend friend = fd.findById(1); // FriendSub sub = fd.findById(1); // compile-time error System.out.println(friend); } } interface BaseEntity {} class Friend implements BaseEntity {} class FriendSub extends Friend {} abstract class BaseDAO<T extends BaseEntity> { abstract String tableName(); abstract String[] columns(); abstract T logicalFromPhysical(Cursor c); public T findById(int id) { List<T> result = new Finder<T>() .where("id = " + id) .findAll(this); if (result.size() > 0) { return result.get(0); } return null; } } class FriendDAO extends BaseDAO<Friend> { @Override public String tableName() { return "friends"; } @Override public final String[] columns() { return new String[] { "id", "name", "age", "favorite_flag" }; } @Override Friend logicalFromPhysical(Cursor c) { return new Friend(); } } class Finder<T extends BaseEntity> { Database db = new Database(); String selection; public Finder<T> where(String selection) { this.selection = selection; return this; } public List<T> findAll(BaseDAO<T> dao) { List<T> result_list = new ArrayList<T>(); Cursor c = db.query( dao.tableName(), dao.columns(), selection); while (c.moveToNext()) { T entity = dao.logicalFromPhysical(c); result_list.add(entity); } c.close(); return result_list; } } class Database { Cursor query(Object... o) { return new Cursor(); } } class Cursor { int i = 0; boolean moveToNext() {return i++ != 1;}; void close() {} }
同エントリのブクマコメントより。
ORM作るとき困る
2012/05/22 fslasht
説得力のある一言。
でも、あの裏技は問題がありそう。
Java 5 の型推論が分からない - IT戦記