流れるようなインターフェースとメソッドチェーンは違うもの

最近はずっと.NETを使っているのでJava界隈の話に疎いのですが、DBアクセスの最適化は業務アプリでは非常に重要なポイントなので上記の内容は興味深いですね。開発者の視点にたっていろいろ考えられて点は素晴らしいし、非常に同意します。

.NETであれば次のC#3.0がExpressionをサポートするので検索条件式などすべてタイプセーフな世界で表現できるし、さらにLINQを使えばより抽象的なDSLとして検索文を記述できるのでこのような心配はないのかなと考えつつ。でもまだ少し先の世界なので、C#2.0現時点でのソリューションを考えてみることにします。今回の中で一番気になったのは検索条件の作成で特にWhere句を流れるようなインターフェースで作成するアイデアについてです。

このアイデアはSimpleWhereというもので実現されているようです。ただ、OR条件がどうもネックになっているようです。この問題を流れるようなインターフェースで対処しようとするとどうしてもOrBegin().Eq("x",100).Eq("y",10).OrEnd()のようなブロック制御するような仕組みが必要になりそうで本来の目的である可読性が悪くなってしまうので辛いところです。これであればネストしたSimpleWhereの方がまだ良いように思います。なかなか難しいですね。

他に何か方法ないかなと考えてみて浮かんだアイデアは、AND・ORをC#演算子オーバーロードで定義してしまうアイデアです。この発想はLinqC#演算子SQL文に変換するアイデアを拝借したものです。さっそく、以前作成した簡易ORMフレームワークに試しに組み込んで見てみました。

[TestMethod]
public void 演算子オーバーロードで検索条件を組み立てる()
{
    QueryObject q = new QueryObject();
    q.Filter = (q.Eq("A", 100) | q.NotEq("B", 200) | !q.Eq("C", 200))
                            & (q.Between("D", 100, 200));

    QueryObjectBuilder b1 = new SqlQueryObjectBuilder(q);
    Assert.AreEqual(" Where ((([A] = @p1) Or ([B] <> @p2)) Or (Not ([C] = @p3)))
 And ([D] Between @p4 And @p5)", b1.QueryText);
}

流れるようなインターフェースではありませんが実用的でいい感じですね。実装も簡単ですしね。これは使えそうなので他の演算子を定義してぜひ組み込むことにしたいと思います。

試しに、流れるようなインターフェースも組み込んでみました。明示的にAndの接頭子を付けてみましたがもう少しAPIは練った方がよさそうですね。今回はQueryObjectのメソッドとして定義しましたが、SimpleWhereのように別クラスに定義してメソッドを単純化するかアイデアはありますね。ただ、QueryObject自体にある方が流れるようなインターフェースになるので、役割インターフェースを導入するなども含めて要検討というところでしょうか。

[TestMethod]
public void AddFilter()
{
    QueryObject qo1 = new QueryObject()
        .AndEq("A", 100)
        .AndNotEq("B", 200)
        .AndLessThanEq("F", DateTime.Now.AddDays(1))
        .AndBetween("I", 100, 200)
        .AndIsNull("J");

    QueryObjectBuilder b1 = new SqlQueryObjectBuilder(qo1);
    Assert.AreEqual(" Where ((((([A] = @p1)) And ([B] <> @p2)) And ([F] <= @p3)) 
And ([I] Between @p4 And @p5)) And ([J] Is Null)", b1.QueryText);
}