【enum】メソッドの定義(3)−strategyパターンを使う方法
前回、インターフェースを使って enum のメソッドを定義する方法を説明しました。
今回は、strategy パターンを使って enum のメソッドを定義する方法を説明します。
今回の仕様
今回は、とある会社の営業月を表す BusinessMonth クラス(enum)を定義することとします。
この会社は、夏のリゾート地で営業している会社としましょう。
で、月毎に料金を以下のように設定したいとしましょう。
- 7月と8月は、2割増とする。
- 11月〜2月は、1割引とする。
- その他の月は、標準価格とする。(割引や割増なし)
実装例−constant specific method implementation の場合
最初に説明した、constant specific method implementation で実装してみることにしましょう。
public enum BusinessMonth { // 1月 JANUARY { public double calculateFee(double base) { return base * 0.9; } }, // 2月 FEBRUARY { public double calculateFee(double base) { return base * 0.9; } }, // 3月 MARCH { public double calculateFee(double base) { return base; } }, // 4月 APRIL { public double calculateFee(double base) { return base; } }, // 5月 MAY { public double calculateFee(double base) { return base; } }, // 6月 JUNE { public double calculateFee(double base) { return base; } }, // 7月 JULY { public double calculateFee(double base) { return base * 1.2; } }, // 8月 AUGUST { public double calculateFee(double base) { return base * 1.2; } }, // 9月 SEPTEMBER { public double calculateFee(double base) { return base; } }, // 10月 OCTOBER { public double calculateFee(double base) { return base; } }, // 11月 NOVEMBER { public double calculateFee(double base) { return base * 0.9; } }, // 12月 DECEMBER { public double calculateFee(double base) { return base * 0.9; } }; // 料金を算出する。 public abstract double calculateFee(double base); }
constant specific method implementation のよろしくないところ
今回のケースでは、料金の算出方法が同じ月がいくつかあります。
通常このようなケースでは、メソッドを共有したいと思うのがプログラマの性ではないでしょうか。
ですが、constant specific method implementation を適用すると、全定数で abstract メソッドを実装しなければいけません。
共通のヘルパーメソッドを呼び出すように定義すればロジックを共有することはできますが、それでも全定数でメソッドを実装しなければいけないことには変わりありません。
1つ1つメソッドを定義するのではなくて、定数毎に実装を簡単に切り替えられればよいのに…
こういう場合に、Strategy パターンが威力を発揮します。
Strategy の適用手順
特に enum で Strategy パターンを適用する際の手順を列挙します。
- Strategy を表す enum の定義
- Strategy を表す enum を、インナークラスと同じ要領で定義します。
- Strategy パターン用の abstract メソッドを定義します。
- Strategy の個々の定数で、abstract メソッドを定義します。
- 元の enum の修正
Strategy を表す enum を定義する部分は、constant specific method implementation と同じです。
Strategy の適用例
BusinessMonth クラスに Strategy パターンを適用した例を、以下に示します。
public enum BusinessMonth { // (2-4)処理内容に応じて Strategy enum をセットするよう、 // 定数を修正する。 // 1月 JANUARY(CalculateRate.WINTER_RATE), // 2月 FEBRUARY(CalculateRate.WINTER_RATE), // 3月 MARCH(CalculateRate.NORMAL_RATE), // 4月 APRIL(CalculateRate.NORMAL_RATE), // 5月 MAY(CalculateRate.NORMAL_RATE), // 6月 JUNE(CalculateRate.NORMAL_RATE), // 7月 JULY(CalculateRate.SUMMER_RATE), // 8月 AUGUST(CalculateRate.SUMMER_RATE), // 9月 SEPTEMBER(CalculateRate.NORMAL_RATE), // 10月 OCTOBER(CalculateRate.NORMAL_RATE), // 11月 NOVEMBER(CalculateRate.WINTER_RATE), // 12月 DECEMBER(CalculateRate.WINTER_RATE); // (2-1)Strategy を表す enum を、 // インスタンス変数として定義する。 private CalculateRate calculateRate; // (2-2)Strategy を表す enum を、 // コンストラクタで設定するようにする。 private BusinessMonth(CalculateRate calculateRate) { this.calculateRate = calculateRate; } // (2-3)abstract メソッドを、 // Strategy を表す enum のメソッドを呼び出すように変更する。 public double calculateFee(double base) { return base * this.calculateRate.getRate(); } // (1)Strategy を表す enum の定義 // (1-1)Strategy を表す enum を、 // インナークラスと同じ要領で定義する。 // 割引率を表す enum。 private enum CalculateRate { // (1-3)Strategy 用のメソッドを実装する。 // 夏 SUMMER_RATE { double getRate() { return 1.2; } }, // 冬 WINTER_RATE { double getRate() { return 0.9; } }, // 通常 NORMAL_RATE { double getRate() { return 1; } }; // (1-2)Strategy 用のメソッドを定義する。 // 割引率を返す。 abstract double getRate(); } }