SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

デザインパターンの使い方

デザインパターンの使い方: Composite

Compositeパターンで複合条件をスマートに表現

  • このエントリーをはてなブックマークに追加

本稿ではデザインパターンのうちの1つ、「Compositeパターン」について解説します。Compositeパターンを利用することで、クライアントコードは複合オブジェクトを非複合オブジェクトと同じように扱うことができます。

  • このエントリーをはてなブックマークに追加

Compositeパターンの例

 コンポジット(composite)とは、他の複数のオブジェクトからなる複合物のことです。Compositeパターンは、クライアントコードが複合オブジェクトを非複合オブジェクトと同じように扱うことのできる状況で使われます。

 本稿のサンプルでは、SQLの複合条件を表現するという要件をCompositeパターンでいかにスマートに実現できるかを示します。SQLの単体の条件句(例えばname like 'abc%'など)は、andなどの接続詞を使って結合できます。従って、and式はlikeなどの句から成る複合句と考えられます。

 コンポジットの組み立て方を紹介するために、まず低レベルで必要とされるオブジェクトを実装することにします。

 最初にColumnクラスで列の名前をカプセル化します。各種のSQLステートメントを構築するときに必要な列の詳細情報は、このColumnクラスのサブクラスで定義します。例えば、StringColumnクラスでは、SQLジェネレータオブジェクトがcreate tableステートメントの構築に使用するかもしれないlengthフィールドを定義します。

 さらに重要なことは、ColumnクラスでsqlValueという抽象メソッドを定義することです。テキスト列を表すサブクラスのメソッド実装では、SQLのwhere句で使用する値文字列を返すようにします。例えば、where name = 'abc'句の値文字列は'abc'となります。一方、数値列を表すサブクラスでは、値を一重引用符で囲みません。where amount = 10のSQL値は10となります。

public abstract class Column {
   private String name;

   public Column(String name) {
      this.name = name;
   }

   String getName() {
      return name;
   }

   abstract String sqlValue(Object value);
}

public class StringColumn extends Column {
   private int length;

   public StringColumn(String name, int length) {
      super(name);
      this.length = length;
   }

   public int getLength() {
      return length;
   }

   public String sqlValue(Object value) {
      return "'" + value + "'";
   }
}

public class NumericColumn extends Column {
   public NumericColumn(String name) {
      super(name);
   }

   public String sqlValue(Object value) {
      return value.toString();
   }
}

 このような列のクラス階層ができていれば、単純な等価比較を行うwhere句の生成テストは簡単に記述できます。

import static org.junit.Assert.*;
import org.junit.*;

public class EqualsTest {
   @Test
   public void stringColumn() {
      Column column = new StringColumn("name", 10);
      Equals criteria = new Equals(column, "joe");
      assertEquals("name = 'joe'", criteria.sqlString());
   }

   @Test
   public void numericColumn() {
      Column column = new NumericColumn("amount");
      Equals criteria = new Equals(column, 5);
      assertEquals("amount = 5", criteria.sqlString());
   }
}

 Equalsクラスの実装は次のとおりです。

public class Equals {
   private Column column;
   private Object value;

   public Equals(Column column, Object value) {
      this.column = column;
      this.value = value;
   }

   public String sqlString() {
      return String.format("%s = %s",
         column.getName(), column.sqlValue(value));
   }
}

 とても単純です。

2つの等価比較を組み合わせたwhere句の生成

 今度は、次のような2つの等価比較を組み合わせたwhere句を生成したいと思います。

name = 'Joe' and amount = 10

 このテストを行うAndTestクラスは次のようになります。

import static org.junit.Assert.*;
import org.junit.*;

public class AndTest {
   @Test
   public void and() {
      Equals name = new Equals(new StringColumn("name", 1), "Joe");
      Equals amount = new Equals(new NumericColumn("amount"), 5);
      And and = new And(name, amount);
      assertEquals("name = 'Joe' and amount = 5", and.sql());
   }
}

 Andクラスの実装も非常に単純です。

public class And {
   private Equals left;
   private Equals right;

   public And(Equals left, Equals right) {
      this.left = left;
      this.right = right;
   }

   public String sql() {
      return left.sqlString() + " and " + right.sqlString();
   }
}

 同様にして、where句で使用するその他の条件にも簡単に対応できます。例えば、likeの実装は次のようになります。

// LikeTest.java:
import static org.junit.Assert.*;
import org.junit.*;

public class LikeTest {
   @Test
   public void simple() {
      Like like = new Like(new StringColumn("name", 1), "Joe%");
      assertEquals("name like 'Joe%'", like.sql());
   }
}

// Like.java:
public class Like {
   private StringColumn column;
   private String value;

   public Like(StringColumn column, String value) {
      this.column = column;
      this.value = value;
   }

   public String sql() {
      return String.format("%s like %s",
         column.getName(), column.sqlValue(value));
   }
}

会員登録無料すると、続きをお読みいただけます

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

次のページ
AndクラスとCriteriaインターフェイス

この記事は参考になりましたか?

  • このエントリーをはてなブックマークに追加
デザインパターンの使い方連載記事一覧

もっと読む

この記事の著者

japan.internet.com(ジャパンインターネットコム)

japan.internet.com は、1999年9月にオープンした、日本初のネットビジネス専門ニュースサイト。月間2億以上のページビューを誇る米国 Jupitermedia Corporation (Nasdaq: JUPM) のニュースサイト internet.comEarthWeb.com からの最新記事を日本語に翻訳して掲載するとともに、日本独自のネットビジネス関連記事やレポートを配信。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

Jeff Langr(Jeff Langr)

本格的なソフトウェアの開発に四半世紀以上携わってきたベテランのソフトウェア開発者。『Agile Java: Crafting Code With Test-Driven Development』(Prentice Hall、2005年)と、他の1冊の著書がある。『Clean Code』(Uncle Bob Martin著、Prentice Hall、2008年8月)にも寄稿している。また、ソフトウェア開発に関する記事を80件以上執筆しており、そのうちの...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/2818 2008/08/22 15:54

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング