SHOEISHA iD

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

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

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

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

Template Methodでクラス間の重複をなくす

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

「Template Methodパターン」は、アルゴリズムの途中で必要な処理を抽象メソッドに委ね、その実装を変えることで処理が変えられるようにするデザインパターンです。本稿では、実際にリファクタリングを行う具体的な例を見ながら、「Template Methodパターン」の使用方法を説明していきます。

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

Template Methodパターンの例

 デザインパターンは強力なツールです。強力なツールには常に誤用の可能性があります。注意を怠ると、ぐちゃぐちゃで何だかよくわからないものを作ってしまったりする危険性があります。

 Template Methodは、2つ以上の関連するクラスの間での重複をなくすための、1つの解決策を提供するデザインパターンです。これはGammaらによる『Design Patterns』(1995年)で取り上げられていた23個のパターンのうちの1つです。

 Template Methodパターンはリファクタリングの目標と考えるのがよいでしょう。本稿では、この目標に向けてコードを手直しする方法を、具体的な例を見ながら説明していきます。「Template Methodパターンとは何ぞや」ではなく、実際にリファクタリングを行い、その結果について語ることにします。

 今回のサンプルでは、スキャンしたフォームを管理するためのルールベースのワークフローシステムを構築します。最初に取り組むルールでは、フォーム上で指定されたステートが、引数として渡されるステートと一致していることが求められます。このルールを表すCheckStateRuleクラスでは、次のようなRuleインターフェースを実装します。

import java.util.*;

public interface Rule {
   boolean eval(Case c, List<String> val, boolean returnTrue);
}

 ワークフローシステムはルールを実行します。ルールを実行することにより、ワークフローの中でフォームを動かすことができます。

 CheckStateRuleクラスのコードは単純です。ルールエンジンはevalメソッドにCaseオブジェクトと引数リストと否定フラグを渡します。このCaseオブジェクトはフォームデータを間接的に含んでいます。switchReturnValuesメソッドは、少し分かりにくい部分ですが、誰かがワークフローシステム内でそれを否定に設定している場合はルールの結果を反転させます。

import java.util.*;

/**
 *
 * Check if the state the case is located in 
 * is equal to argument passed in.
 */
public class CheckStateRule implements Rule {
   private static final String EXCEPT = "Exception: ";
   String errStr;
   boolean hasMissingData;

   public boolean eval(Case c, List<String> val, boolean returnTrue){
      boolean returnVal = false;
      String argVal = null;

      try {
         if (val == null || val.size() != 1) {
            errStr = "Can't convert value list to a state";
            hasMissingData = true;
            return false;
         } else {
            argVal = (String)val.get(0);
         }

         // get the location object off the form 
         // and get the state the case resides in
         Location loc = (Location)c.getFormComposite("location");
         if (loc == null) {
            errStr
             = "Cannot determine the state this case is located in.";
            hasMissingData = true;
            return false;
         }
         String st = loc.getAttribute("state");
         String state = (st == null ? null : st.toUpperCase());
         if (state == null) {
            errStr
             = "Cannot determine the state this case is located in.";
            hasMissingData = true;
            return false;
         } else if (state.equals(argVal)) {
            returnVal = true;
         }

         // return value
         List<Object> returns
               = switchReturnValues(returnTrue, returnVal,
                 "Case does not reside in '" + argVal + "'",
                 "Case resides in '" + argVal + "'");
         returnVal = ((Boolean)returns.get(0)).booleanValue();
         errStr = (String)returns.get(1);

      } catch (Exception e) {
         errStr = EXCEPT + e.getMessage();
         hasMissingData = true;
         return false;
      }

      return returnVal;

   }

   public List<Object> switchReturnValues(boolean returnTrue,
         boolean returnVal, String falseMsg, String trueMsg)
         throws Exception {

      if (!returnTrue)
         returnVal = !returnVal;

      String retErrStr = null;
      if (!returnVal) {
         if (returnTrue)
            retErrStr = falseMsg;
         else
            retErrStr = trueMsg;
      }

      ArrayList<Object> returns = new ArrayList<Object>();
      returns.add(new Boolean(returnVal));
      returns.add(retErrStr);
      return returns;
   }

   public boolean isMissingData() {
      return hasMissingData;
   }

   public String getError() {
      return errStr;
   }
}

 CheckStateRuleクラスができたら、続いて2番目のルールであるCheckDependentsRuleクラスに着手します。このルールの機能は、フォーム上で指定された人物が間違いなく姓と名の両方を持つようにすることです。

import java.util.*;

/**
 *
 * Ensure all dependent detail is supplied
 */
public class CheckDependentsRule implements Rule {
   private static final String EXCEPT = "Exception: ";
   private String errorMessage;
   private boolean missingData = false;

   public boolean eval(Case c, List<String> val, boolean returnTrue){
      boolean returnVal = false;

      try {
         // get the location object off the form 
         // and get the state the case resides in
         Subscriber sub
           = (Subscriber)c.getFormComposite("subscriber");
         if (sub == null) {
            errorMessage = "Unable to obtain subscriber info.";
            missingData = true;
            return false;
         }

         returnVal = true;
         for (int i = 1; i <= Subscriber.DEPENDENT_ROWS; i++) {
            if (!hasCompleteDependentName(sub, i)) {
               returnVal = false;
               break;
            }
         }

         boolean returnVal1 = returnVal;
         if (!returnTrue)
            returnVal1 = !returnVal1;

         String retErrStr = null;
         if (returnVal1 == false) {
            if (returnTrue)
               retErrStr = "Dependent info incomplete";
            else
               retErrStr = "All dependent info provided";
         }

         List<Object> res = new ArrayList<Object>();
         res.add(new Boolean(returnVal1));
         res.add(retErrStr);

         // return value
         List<Object> returns = res;
         returnVal = ((Boolean)returns.get(0)).booleanValue();
         errorMessage = (String)returns.get(1);

      } catch (Exception e) {
         errorMessage = EXCEPT + e.getMessage();

         missingData = true;
         return false;
      }

      return returnVal;

   }

   private boolean hasCompleteDependentName(Subscriber sub,
                                                int number) {
      String firstName = sub.getAttribute("dep" + number + "first");
      String lastName = sub.getAttribute("dep" + number + "last");

      if ((!isEmpty(firstName) && isEmpty(lastName)) ||
            (!isEmpty(lastName) && isEmpty(firstName))) {
         errorMessage
          = "Complete name must be specified for dependent "
            + number;
         return false;
      }
      return true;
   }

   private boolean isEmpty(String string) {
      return string == null || string.equals("");
   }

   public boolean isMissingData() {
      return missingData;
   }

   public String getError() {
      return errorMessage;
   }
}

次のページ
クラス間の重複部分を無くす

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

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

もっと読む

この記事の著者

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件以上執筆しており、そのうちの...

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

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

この記事をシェア

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

おすすめ

アクセスランキング

アクセスランキング

イベント

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

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

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

メールバックナンバー

アクセスランキング

アクセスランキング