top / index / prev / next / target / source

2007-07-04 diary: FizzBuzz 問題 - デザインパターン適用版

いがぴょんの日記 日記形式でつづる いがぴょんコラム ウェブページです。

old-v2

FizzBuzz 問題 - デザインパターン適用版

プロのJavaプログラマーのなかには、デザインパターンを適用したがる人がいます。(デザインパターン魔 という用語を 思い浮かべる方もいらっしゃることでしょう)

FizzBuzz 問題 - デザインパターン適用版

2007.07.05執筆

プロのJavaプログラマーのなかには、デザインパターンを適用したがる人がいます。(デザインパターン魔 という用語を 思い浮かべる方もいらっしゃることでしょう)

※注意: ここで挙げている例は ブラックユーモアです。

まず、「学校における数遊びゲームのルール」という抽象的な概念を抽出するという おこないを実施してみましょう。 FizzBuzzPattern01GameRule.java

/**
 * どこかの学校の子供たちの遊び
 */
public interface FizzBuzzPattern01GameRule {
        /**
         * 与えられた数に対応する 特定のゲームにおけるメッセージを取得する。
         * 
         * @param argNumber
         *            対象となる数。
         * @return 特定のゲームにおけるメッセージ。
         */
        public String getMessage(final int argNumber);
}

イギリスの学校の子供たちの遊び FizzBuzzは 数字を使った遊びの一つとして インタフェースを実装します。 FizzBuzzPattern01Rule.java

/**
 * FizzBuzz: イギリスの学校の子供たちの遊び
 * 
 * 1から100までの数を取得する。<br>
 * ・3の倍数のときは数の代わりに「Fizz」を取得する。<br>
 * ・5の倍数のときは数の代わりに「Buzz」を取得する。<br>
 * ・3と5両方の倍数の場合には数の代わりに「FizzBuzz」を取得する。
 */
public class FizzBuzzPattern01Rule implements FizzBuzzPattern01GameRule {
        /**
         * 与えられた数に対応する FizzBuzzメッセージを取得する。
         * 
         * ・3の倍数のときは「Fizz」を戻す。<br>
         * ・5の倍数のときは「Buzz」を戻す。<br>
         * ・3と5両方の倍数の場合には「FizzBuzz」を戻す。<br>
         * ・いずれにも該当しない場合には数を戻す。
         * 
         * @param argNumber
         *            対象となる数。
         * @return メッセージ。
         *         3の倍数のときは「Fizz」、5の倍数のときは「Buzz」、3と5両方の倍数の場合には「FizzBuzz」、いずれにも該当しない場合には数。
         */
        public String getMessage(final int argNumber) {
                // 3の倍数であるかどうか。
                final boolean isMultipleOf3 = (argNumber % 3 == 0);
                // 5の倍数であるかどうか。
                final boolean isMultipleOf5 = (argNumber % 5 == 0);

                if (isMultipleOf3 && isMultipleOf5) {
                        // 3と5両方の倍数の場合には「FizzBuzz」を戻す。
                        return "FizzBuzz";
                } else if (isMultipleOf3) {
                        // 3の倍数のときは数の代わりに「Fizz」を戻す。
                        return "Fizz";
                } else if (isMultipleOf5) {
                        // 5の倍数のときは「Buzz」を戻す。
                        return "Buzz";
                } else {
                        // いずれにも該当しない場合には数を戻す。
                        return String.valueOf(argNumber);
                }
        }
}

ルールを利用する際には インスタンス生成の実行コストを考慮し、ループの外側でインスタンスを生成するようにします。 FizzBuzzPattern01.java

/**
 * FizzBuzz: イギリスの学校の子供たちの遊び
 * 
 * 1から100までの数をプリントする。<br>
 * ・3の倍数のときは数の代わりに「Fizz」とプリントする。<br>
 * ・5の倍数のときは数の代わりに「Buzz」とプリントする。<br>
 * ・3と5両方の倍数の場合には数の代わりに「FizzBuzz」とプリントする。
 */
public class FizzBuzzPattern01 {
        /**
         * エントリポイント。
         * 
         * @param args
         *            コマンドライン引数。このプログラム内では利用されない。
         */
        public static void main(final String[] args) {
                new FizzBuzzPattern01().process();
        }

        /**
         * このクラスの主処理。
         */
        public void process() {
                // TODO:ゲームのルールを別に分け、またインタフェースを抽出しました。
                final FizzBuzzPattern01GameRule rule = new FizzBuzzPattern01Rule();

                for (int number = 1; number <= 100; number++) {
                        // 生成された文字列をプリントする。
                        System.out.println(rule.getMessage(number));
                }
        }
}

インタフェースを導出し、ここで満足するプログラマーがいます。

何かしらデザインパターン適用などによるリファクタリングを実施しおわると、次に他に適用可能なデザインパターンを探すのが習性という Javaプログラマーがいます。

ここではファクトリクラスを導入してみましょう。 FizzBuzzPattern02GameRuleFactory.java

/**
 * 学校の子供たちの遊びを取得するファクトリクラス。
 */
public class FizzBuzzPattern02GameRuleFactory {
        /**
         * FizzBuzzゲームルールを取得する。
         * 
         * @return ゲームルール。
         */
        public static FizzBuzzPattern02GameRule getFizzBuzzRuleInstance() {
                return FizzBuzzPattern02Rule.getInstance();
        }
}

インタフェースのクラスは 特に変化ありません。 FizzBuzzPattern02GameRule.java

/**
 * どこかの学校の子供たちの遊び
 */
public interface FizzBuzzPattern02GameRule {
        /**
         * 与えられた数に対応する 特定のゲームにおけるメッセージを取得する。
         * 
         * @param argNumber
         *            対象となる数。
         * @return 特定のゲームにおけるメッセージ。
         */
        String getMessage(final int argNumber);
}

ファクトリからインスタンスを取得するので、本体のコンストラクタは隠蔽化しておきます。 FizzBuzzPattern02Rule.java

/**
 * FizzBuzz: イギリスの学校の子供たちの遊び
 * 
 * 1から100までの数を取得する。<br>
 * ・3の倍数のときは数の代わりに「Fizz」を取得する。<br>
 * ・5の倍数のときは数の代わりに「Buzz」を取得する。<br>
 * ・3と5両方の倍数の場合には数の代わりに「FizzBuzz」を取得する。
 */
public class FizzBuzzPattern02Rule implements FizzBuzzPattern02GameRule {
        /**
         * コンストラクタを隠蔽化する。
         */
        private FizzBuzzPattern02Rule() {
        }

        /**
         * このクラスのインスタンスを取得する。
         * 
         * @return 新規に作成されたインスタンス。
         */
        static FizzBuzzPattern02GameRule getInstance() {
                return new FizzBuzzPattern02Rule();
        }

        /**
         * 与えられた数に対応する FizzBuzzメッセージを取得する。
         * 
         * ・3の倍数のときは「Fizz」を戻す。<br>
         * ・5の倍数のときは「Buzz」を戻す。<br>
         * ・3と5両方の倍数の場合には「FizzBuzz」を戻す。<br>
         * ・いずれにも該当しない場合には数を戻す。
         * 
         * @param argNumber
         *            対象となる数。
         * @return メッセージ。
         *         3の倍数のときは「Fizz」、5の倍数のときは「Buzz」、3と5両方の倍数の場合には「FizzBuzz」、いずれにも該当しない場合には数。
         */
        public String getMessage(final int argNumber) {
                // 3の倍数であるかどうか。
                final boolean isMultipleOf3 = (argNumber % 3 == 0);
                // 5の倍数であるかどうか。
                final boolean isMultipleOf5 = (argNumber % 5 == 0);

                if (isMultipleOf3 && isMultipleOf5) {
                        // 3と5両方の倍数の場合には「FizzBuzz」を戻す。
                        return "FizzBuzz";
                } else if (isMultipleOf3) {
                        // 3の倍数のときは数の代わりに「Fizz」を戻す。
                        return "Fizz";
                } else if (isMultipleOf5) {
                        // 5の倍数のときは「Buzz」を戻す。
                        return "Buzz";
                } else {
                        // いずれにも該当しない場合には数を戻す。
                        return String.valueOf(argNumber);
                }
        }
}

すると、ゲームのルールが変わっても 容易に切り替えを実現可能なクラスが出来上がります! FizzBuzzPattern02.java

/**
 * FizzBuzz: イギリスの学校の子供たちの遊び
 * 
 * 1から100までの数をプリントする。<br>
 * ・3の倍数のときは数の代わりに「Fizz」とプリントする。<br>
 * ・5の倍数のときは数の代わりに「Buzz」とプリントする。<br>
 * ・3と5両方の倍数の場合には数の代わりに「FizzBuzz」とプリントする。
 */
public class FizzBuzzPattern02 {
        /**
         * エントリポイント。
         * 
         * @param args
         *            コマンドライン引数。このプログラム内では利用されない。
         */
        public static void main(final String[] args) {
                new FizzBuzzPattern02().process();
        }

        /**
         * このクラスの主処理。
         */
        public void process() {
                // TODO:ゲームのルールをファクトリクラスから取得するように変更しました。
                final FizzBuzzPattern02GameRule rule = FizzBuzzPattern02GameRuleFactory
                                .getFizzBuzzRuleInstance();

                for (int number = 1; number <= 100; number++) {
                        // 生成された文字列をプリントする。
                        System.out.println(rule.getMessage(number));
                }
        }
}

デザインパターンを適用し、ここで満足するプログラマーがいます。しかし これで飽きたらず また違うアイデアが次々に浮かんでくるものです。

FizzBuzz程度の規模でも (デザインパターン魔は) デザインパターンを適用してしまいそうになるものなのです。

関連する日記


この日記について