Shoeisha Technology Media

CodeZine(コードジン)

特集ページ一覧

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

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

  • ブックマーク
  • LINEで送る
  • このエントリーをはてなブックマークに追加
2008/08/13 14:00

本稿ではデザインパターンのうちの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));
   }
}

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

著者プロフィール

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

    japan.internet.com は、1999年9月にオープンした、日本初のネットビジネス専門ニュースサイト。月間2億以上のページビューを誇る米国 Jupitermedia Corporation (Nasdaq: JUPM) のニュースサイト internet.com や EarthWeb.c...

  • Jeff Langr(Jeff Langr)

    本格的なソフトウェアの開発に四半世紀以上携わってきたベテランのソフトウェア開発者。『Agile Java: Crafting Code With Test-Driven Development』(Prentice Hall、2005年)と、他の1冊の著書がある。『Clean Code』(Uncle...

バックナンバー

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

もっと読む

All contents copyright © 2005-2019 Shoeisha Co., Ltd. All rights reserved. ver.1.5