【デザインパターン】【Java】Decorator パターン ~装飾者~

 ■ Decoratorパターン

 * 既存クラスのメソッドに対して、継承を使わずに
   ラッパークラス(機能を一つひとつかぶせていくイメージ)を作ることで
   機能追加を行うデザインパターン

 ■ 使いどころ

 * 機能を追加(拡張)する場合

 ■ 登場人物

 Component (部品)

 * 機能追加の核となるクラス
 * 機能拡張するメソッドのインタフェースを定義

 ConcrenteComponent (具体的な部品)

 * 基本機能を実装
 * Component を継承

 Decorator (装飾者)

 * 「Component」を実装しているオブジェクトを保持

 ConcreteDecorator (具体的な装飾者)

 * Decorator を継承

 Client (依頼者)

 * 利用者

 ■ サンプル

 * ベースとなるPriceとカスタマイズをIPriceインタフェースで同一視することにより、
   さまざまな組み合わせのカスタマイズが可能になる

 IPrice.java : Component (部品)

import java.math.BigDecimal;

public interface IPrice {
   BigDecimal getPrice();
   void setPrice(BigDecimal priceValue);
}

 Price.java : ConcrenteComponent (具体的な部品)

import java.math.BigDecimal;

public class Price implements IPrice {
   private BigDecimal priceValue;
   
   @Override
   public BigDecimal getPrice() {
      return this.priceValue;
   }

   @Override
   public void setPrice(BigDecimal priceValue) {
      this.priceValue = priceValue;
   }
}

 PriceDecorator.java : Decorator (装飾者)

public abstract class PriceDecorator implements IPrice {
   protected IPrice price;
   
   public PriceDecorator(IPrice price) {
      this.price = price;
   }
}

 PriceFormatter.java : ConcreteDecorator (具体的な装飾者)

import java.math.BigDecimal;

public class PriceFormatter extends PriceDecorator {

   public enum RoundingType {
      None,
      // 四捨五入
      RoundOff,
      // 切上げ
      Revaluation,
      // 切捨て
      RoundDown,
   }
   
   private RoundingType type;
   
   public PriceFormatter(IPrice price, RoundingType type) {
      super(price);
      this.type = type;
   }

   @Override
   public BigDecimal getPrice() {
      BigDecimal priceValue = super.price.getPrice();
      
      switch (this.type) {
      case RoundOff:
         priceValue = priceValue.setScale(1, BigDecimal.ROUND_HALF_UP); 
         break;
      case Revaluation:
         priceValue = priceValue.setScale(1, BigDecimal.ROUND_UP);
         break;
      case RoundDown:
         priceValue = priceValue.setScale(1, BigDecimal.ROUND_DOWN);
         break;
      case None:
      default:
         break;
      }
      return priceValue;
   }

   @Override
   public void setPrice(BigDecimal priceValue) {
      super.price.setPrice(priceValue);
   }
}

 Main.java : Client (依頼者)

import java.math.BigDecimal;
import com.decorator.PriceFormatter.RoundingType;

public class Main {

   public static void main(String[] args) {
      Price price = new Price();
      price.setPrice(BigDecimal.valueOf(42.195));
      
      PriceFormatter formatter1 =
            new PriceFormatter(price, RoundingType.Revaluation);
      System.out.println("切り上げ " + formatter1.getPrice());

      PriceFormatter formatter2 =
            new PriceFormatter(price, RoundingType.RoundDown);
      System.out.println("切捨て " + formatter2.getPrice());
      
      PriceFormatter formatter3 =
            new PriceFormatter(price, RoundingType.RoundOff);
      System.out.println("四捨五入 " + formatter3.getPrice());
   }
}

 出力結果

切り上げ 42.2
切捨て 42.1
四捨五入 42.2

 参考文献

 Decorator パターン
http://blog.asial.co.jp/541
http://www.mochi-programming-ras.info/contents/Java/GoF/9
http://ja.wikipedia.org/wiki/Decorator_%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3
http://www.itsenka.com/contents/development/designpattern/decorator.html
http://www.sgnet.co.jp/java/java06_05.html
http://sizuki0514.blogspot.jp/2012/12/decorator.html
http://www.h7.dion.ne.jp/~a.d.1976/design20090121.html
 お金などの数に関する型
http://www.atmarkit.co.jp/ait/articles/0702/27/news100.html
http://java-reference.sakuraweb.com/java_number_round.html