ページ

2014年4月1日

DaoでのResultSetへのアクセスはenumでいいよね

Java8でラムダが使えれば…。
で、DaoでResultSetへのアクセスでインデックスでアクセスする場合、

int index=0;

dto.setX(resultSet.getInt(++index);
dto.setY(resultSet.getInt(++index);
dto.setZ(resultSet.getInt(++index);

ってアクセスさせるとメンテナンス性が落ちるよね、って話しでした。個人的にはカラムでのアクセスで問題ないと思っていますが、インデックスでのアクセスもenumでアクセスさせれば、もう少し、メンテナンス性が上がります。

こんなかんじですね。

enum ColumnName  {
    column1,
    column2,
    column3;

    public int getIndex() {
        return ordinal() + 1;
    }

    public static String getSelectStatement() {
        StringBuilder sb = new StringBuilder();
        for (String s: ColumnName.values()) {
            if (sb.length()>0) {
                 sb.append(",")
             }
             sb.append(s):
          }
     }
}


SQL文を組み立てるときは


    String sql = "SELECT " + ColumnName.getSelectStatetement() + " FROM SOME_TABLE"

ResultSetは、

    dto.setX(rs.getInt(ColumnName.column1));
    dto.setY(rs.getInt(ColumnName.column2));
    dto.setZ(rs.getInt(ColumnName.column3));

ほら。フィールドが追加されてもenumに値をセットするだけで、順番も意識しなくてよくなりました。

まあ、enumに値を追加することに神経を集中してdtoに詰めるのを忘れちゃいそうですね。

で、これがもう少しメンテナンス性は挙げられるようになる、と書いていた理由です。
ということで、技術は楽をするための道具です。言語に新しく追加されたものは、世界中の頭のいい人が、もっと楽をしたいと思って追加されるものです。なので、マゾな人以外は積極的に新しいものを使って、楽をできるところは楽をして、本質的に難しいところに開発者は専念すべきなのです。そのために、新しい技術を追い求めるのです。いやいや、それは嘘です。僕は新しいものが好きだから、追い求めるだけです。


それから、そのうち、リフレクションしたくなってくるかもしれません。でも、それすると、メンテナンス性が更に高まりますが、パフォーマンスは少し落ちます。我慢してください。その次がバイトコード生成ですが、そこまで行くと、もう、あちらの世界の住人になって、もう、戻ってこれません。気をつけてください。

追記
======

最近は朝、家を出る時が夜ではなく朝になってきて、ちょっと嬉しいです。

ということで、昨日の続き。昨日のように慌ただしいタイミングじゃないのでゆっくりと書けます。

まず、前提としてenumはやれば出来る子です。どこかでenumは所詮はクラスだよ、と書いたつもりですが、所詮はクラスなので、クラスでできることは大体出来ます。Effective Javaにはシングルトンもenumでできるよ、って書いていたように思いますが、僕は実際にやったことはないです。

さて、所詮はクラス、されどクラスなので、enumもinterfaceを身にまとってかっこ良く振る舞ってくれます。
例えばこんな感じ。

interface Ge {
    public void urya();
}

enum Ho implements Ho {
    H {
        public void urya() { System.out.println("Hoo"); }
    },
    G {
        public void urya() { System.out.println("Gee"); }
    }
}

で、 Ho.H.urya()って感じで使えます。なので、

for (Ho h: Ho.values) {
    h.urya();
}

で全部回りますね。


じゃあ、本題です。Daoでdtoにブツを詰め込むときに、場所が離れていると詰めるのを忘れがちになります。頑張ればHibernateがやっているようにもっとスマートにできなくもないですが、それならHibernate使えよ、ということになってしまいます。なので、現実的な妥協点は次のような感じ。
変数名とかメソッド名は割と適当なので、適当に置き換えてください。。


interface Binder {
    public void bind(int position, ResultSet rs, Dto dto);
}

enum Column implements Binder {
    FIELD_A {
        public void bind(int position, ResultSet rs, Dto dto) {
            dto.setField_A(rs.getInt(position + 1);
        }
     },
     FIELD_B {
        public void bind(int position, ResultSet rs, Dto dto) {
            dto.setField_B(rs.getString(position + 1);
        }
     };


     public String listFields() {
         StringBuilder sb = new StringBuilder();
         for (Column c: Column.values()) {
             if (sb.length() > 0) {
                 sb.append(",");
              }
              sb.append(c.name());
          }

           return sb.toString();
    }
}


で、もう一度、SQL文を組み立てるときは、

    String sql = "Select " + Column.listFields() + " From Some_table";

Dtoに値を放り込むときは、前回の記事で書いたIndexableIteratorがある前提で

    for (IndxableIterator.Pair<Column> pair: new IndexableIterator<Column>(Column.values())) {
        c.getValue().bind(c.getIndex(), resultSet, dto);
     }

ってやれば関連が強いものが近くに置かれて忘れ難くなった。フィールドが追加されたら、enumのところとdtoに追加すればいい。で、interfaceとenumはDTOの中にInner Classとして宣言すれば、関連性がより強くなって忘れにくい、変更箇所が局所化できますね。
それに、DAOの方はコードの変更が必要なくなるおまけ付き。

Java8のラムダとか、FunctionalInterafaceが入ってくると、各bindメソッドがラムダ式でもっと簡単に書けて、初期化の引数として渡せるので、かなりスッキリするはずです。


ということで、これくらいがJava7までの現実的な妥協点じゃないでしょうか?

最期におさらいをすると、enumはあなたが思っている以上にできる子なのです。

0 件のコメント: