SHOEISHA iD

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

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

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

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

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

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

AndクラスとCriteriaインターフェイス

 前述のAndクラスでは、2つのEqualsオブジェクトの結合のみをサポートしていました。このAndクラスを改良して、Likeオブジェクトや、これから思いつくかもしれない他の条件もサポートするようにしたいと思います。それぞれの条件がSQL表現を返す機能は、共通のインターフェイスとして抜き出すことができます。

public interface Criteria {
   String sqlString();
}

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

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

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

// Equals.java:
public class Equals ''implements Criteria'' {
   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));
   }
}

 Likeクラスを修正して、同じCriteriaインターフェイスを実装するようにします。そのためには、若干のリファクタリングが必要です。メソッド名の不一致があるからです。

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

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

// Like.java
public class Like ''implements Criteria'' {
   private StringColumn column;
   private String value;

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

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

 LikeEqualsの両方でCriteriaインターフェイスを実装したので、この変更内容をAndクラスに反映させます。

// AndTest.java
import static org.junit.Assert.*;
import org.junit.*;

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

// And.java
public class And {
   private ''Criteria'' left;
   private ''Criteria'' right;

   public And(''Criteria'' name, ''Criteria'' amount) {
      this.left = name;
      this.right = amount;
   }

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

 複合物らしいものが全然出てこないじゃないか、とお思いの人もいるかもしれません。しかしちょっと発想を変えてみると、意外な新発見があるものです。

 例えば今回の場合、SQLステートメントについて少し考えてみると、Andはそれ自体が、他の条件と組み合わせることのできる1つの条件であるということに気づきます。例えば、次のようなwhere句が必要になる場合が考えられます。

where (amount = 10 and name like 'Joe%') and department = 'Labor'

 修正したAndクラスを以下に示します。

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

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

// And.java:
public class And ''implements Criteria'' {
   private ''Criteria'' left;
   private ''Criteria'' right;

   public And(''Criteria'' name, ''Criteria'' amount) {
      this.left = name;
      this.right = amount;
   }

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

 これで、like句とequals句とand句を自由に結合できるようになります。

 次のUMLダイアグラムを見ると、このサンプルでCompositeデザインパターンがどのように実装されているかが分かります。鍵となる関係は、AndクラスはCriteriaインターフェイスの実装であり、それと同時に、And型のオブジェクトは複数のCriteriaオブジェクトから成るという点です。クライアントコードでCriteriaオブジェクトの適切な階層を構築しておけば、1つの呼び出しで、この階層の最上位のオブジェクトにsqlStringを要求できます。

 Compositeデザインパターンを使わずに同じ機能をコーディングすることもできますが、その場合は、メソッドのオーバーロードやifステートメントがもっとたくさん必要になるので、システムが複雑になります。

図1 Compositeパターン
図1 Compositeパターン

参考資料

  • 『Design Patterns: Elements of Reusable Object-Oriented Software』 Erich Gamma著、Addison-Wesley Pub、1995年1月

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

連載通知を行うには会員登録(無料)が必要です。
既に会員の方はを行ってください。
デザインパターンの使い方連載記事一覧

もっと読む

この記事の著者

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」など、さまざまなカンファレンスを企画・運営しています。

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

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

メールバックナンバー