天天看點

Java設計模式:裝飾者模式(Decorator Pattern)

裝飾者模式,涉及的重要設計原則:類應該對擴充開放,對修改關閉。

裝飾者模式定義:

裝飾者模式動态地将責任附加到對象上。若要擴充功能,裝飾者提供了比繼承更有彈性的替代方案。

UML類圖:

Java設計模式:裝飾者模式(Decorator Pattern)

裝飾者模式事例:

咖啡店

咖啡種類:

1)深焙咖啡(DarkRoast)

2)家庭混合咖啡(HouseBlend)

3)濃咖啡(Espresso)

4)低咖啡因咖啡(Decaf)

5)其它咖啡。。。

咖啡調料:

1)摩卡(Mocha)

2)牛奶(Milk)

3)豆漿(Soy)

4)奶泡(Whip)

5)其它調料。。。

點單:

要一份加了摩卡和奶泡的深焙咖啡...等等

UML類圖

代碼實作:

Beverage.java

package com.jing.decorator;

/**
 * 所有飲料的抽象超類
 * @author LiuJing
 *
 */
public abstract class Beverage {

  /**
   * 飲料的名字
   */
  String description = "Unknow Beverage";
  
  /*
   * 擷取飲料的名字
   */
  public String getDescription(){
    return description;
  }
  
  
  /***
   * 擷取飲料的價格
   * @return 傳回計算後的總價
   */
  public abstract double cost();
  
}      

DarkRoast.java

package com.jing.decorator;

import java.text.DecimalFormat;

/***
 * 深烤咖啡
 * @author LiuJing
 *
 */
public class DarkRoast extends Beverage {

  /***
   * 構造時确定名字
   */
  public DarkRoast(){
    description = "DarkRoast";
  }
  
  /***
   * 價格
   */
  public double cost() {
    double cost = 10.0;
    
    // 用于儲存2位小數
    DecimalFormat df = new DecimalFormat("#.00");
    return Double.valueOf(df.format(cost));
  }
}      

Decaf.java

package com.jing.decorator;

import java.text.DecimalFormat;

/***
 * 低咖啡因咖啡
 * @author LiuJing
 *
 */
public class Decaf extends Beverage {

  /***
   * 構造時确定名字
   */
  public Decaf() {
    description = "Decaf";
  }
  
  /***
   * 價格
   */
  public double cost() {
    double cost = 11.0;

    DecimalFormat df = new DecimalFormat("#.00");
    return Double.valueOf(df.format(cost));
  }
}      

Espresso.java

package com.jing.decorator;

import java.text.DecimalFormat;

/***
 * 濃咖啡
 * @author LiuJing
 *
 */
public class Espresso extends Beverage {

  /***
   *  構造時确定其名字
   */
  public Espresso(){
    description = "Espresso";
  }
  
  /***
   * 方法傳回 該飲料的價格
   */
  public double cost() {
    double cost = 12.0;
    
    DecimalFormat df = new DecimalFormat("#.00");
    return Double.valueOf(df.format(cost));
  }
}      

HouseBlend.java

package com.jing.decorator;

import java.text.DecimalFormat;

/***
 * 家庭混合咖啡
 * @author LiuJing
 *
 */
public class HouseBlend extends Beverage {

  /***
   * 構造時确定其名字
   */
  public HouseBlend(){
    description = "HouseBlend";
  }
  
  /***
   * 傳回該咖啡的價格
   */
  public double cost() {
    double cost = 13.0;
    
    DecimalFormat df = new DecimalFormat("#.00");
    return Double.valueOf(df.format(cost));
  }

}      

CondimentDecorator.java

package com.jing.decorator;

/***
 * 調料裝飾者抽象超類,繼承至 Beverage,便于類型的統一,能彼此替換。
 * @author LiuJing
 *
 */
public abstract class CondimentDecorator extends Beverage {
  
  /***
   * 用于儲存要裝飾的咖啡對象
   */
  Beverage beverage;
  
  /***
   * 所有子類都必須重新實作 getDescription()方法
   */
  public abstract String getDescription();

}      

Milk.java

package com.jing.decorator;

import java.text.DecimalFormat;

/***
 * 咖啡調料 牛奶  Milk >> CondimentDecorator >> Beverage
 * @author LiuJing
 *
 */
public class Milk extends CondimentDecorator {

  
  /**
   * 構造時确定要加給誰
   * @param beverage
   */
  public Milk(Beverage beverage){
    this.beverage = beverage;
  }
  
  
  /***
   * 不僅顯示目前名,還說明被裝飾的名字 
   */
  @Override
  public String getDescription() {
    // TODO Auto-generated method stub
    return beverage.getDescription() + ", Milk";
  }

  /***
   * 擷取本身的價格和咖啡的價格
   */
  @Override
  public double cost() {
    // TODO Auto-generated method stub
    double cost = beverage.cost();
    cost += 0.1;
    
    DecimalFormat df = new DecimalFormat("#.00");
    return Double.valueOf(df.format(cost));
  }

}      

Mocha.java

package com.jing.decorator;

import java.text.DecimalFormat;

/***
 * 咖啡調料 摩卡 Mocha >> CondimentDecorator >> Beverage
 * 
 * @author LiuJing
 * 
 */
public class Mocha extends CondimentDecorator {


  /**
   * 構造時确定要加給誰
   * 
   * @param beverage
   */
  public Mocha(Beverage beverage) {
    this.beverage = beverage;
  }

  /***
   * 不僅顯示目前名,還說明被裝飾的名字
   */
  @Override
  public String getDescription() {
    // TODO Auto-generated method stub
    return beverage.getDescription() + ", Mocha";
  }

  /***
   * 擷取本身的價格和咖啡的價格
   */
  @Override
  public double cost() {
    // TODO Auto-generated method stub
    double cost = beverage.cost();
    cost += 0.1;

    DecimalFormat df = new DecimalFormat("#.00");
    return Double.valueOf(df.format(cost));
  }

}      

Soy.java

package com.jing.decorator;

import java.text.DecimalFormat;

/***
 * 咖啡調料 豆漿 Soy >> CondimentDecorator >> Beverage
 * 
 * @author LiuJing
 * 
 */
public class Soy extends CondimentDecorator {


  /**
   * 構造時确定要加給誰
   * 
   * @param beverage
   */
  public Soy(Beverage beverage) {
    this.beverage = beverage;
  }

  /***
   * 不僅顯示目前名,還說明被裝飾的名字
   */
  @Override
  public String getDescription() {
    // TODO Auto-generated method stub
    return beverage.getDescription() + ", Soy";
  }

  /***
   * 擷取本身的價格和咖啡的價格
   */
  @Override
  public double cost() {
    // TODO Auto-generated method stub
    double cost = beverage.cost();
    cost += 0.1;

    DecimalFormat df = new DecimalFormat("#.00");
    return Double.valueOf(df.format(cost));
  }

}      

Whip.java

package com.jing.decorator;

import java.text.DecimalFormat;

/***
 * 咖啡調料 奶泡 Whip >> CondimentDecorator >> Beverage
 * 
 * @author LiuJing
 * 
 */
public class Whip extends CondimentDecorator {


  /**
   * 構造時确定要加給誰
   * 
   * @param beverage
   */
  public Whip(Beverage beverage) {
    this.beverage = beverage;
  }

  /***
   * 不僅顯示目前名,還說明被裝飾的名字
   */
  @Override
  public String getDescription() {
    // TODO Auto-generated method stub
    return beverage.getDescription() + ", Whip";
  }

  /***
   * 擷取本身的價格和咖啡的價格
   */
  @Override
  public double cost() {
    // TODO Auto-generated method stub
    double cost = beverage.cost();
    cost += 0.1;

    DecimalFormat df = new DecimalFormat("#.00");
    return Double.valueOf(df.format(cost));
  }

}      

測試類

Test.java

package com.jing.decorator;

public class Test {

  /**
   * @param args
   */
  public static void main(String[] args) {
    // TODO Auto-generated method stub

    
    // 點了一份濃咖啡
    Beverage beverage = new Espresso();
    System.out.println(
        beverage.getDescription() + ": $ " + beverage.cost());

    // 點了一份深烤
    Beverage beverage2 = new DarkRoast();
    beverage2 = new Mocha(beverage2);    // 加 摩卡裝飾
    beverage2 = new Mocha(beverage2);    // 再加 摩卡裝飾
    beverage2 = new Whip(beverage2);     // 再加 奶泡裝飾
    System.out.println(
        beverage2.getDescription() + ": $ " + beverage2.cost());

    // 點了一份家庭混合
    Beverage beverage3 = new HouseBlend();
    beverage3 = new Soy(beverage3);   // 加 豆漿裝飾
    beverage3 = new Mocha(beverage3); // 再加 摩卡裝飾
    beverage3 = new Whip(beverage3);  // 再加 奶泡裝飾
    System.out.println(
        beverage3.getDescription() + ": $ " + beverage3.cost());

    // 用 工廠模式 和 生成器模式,可以更好的建立被 裝飾者對象
  }

}      

輸出:

Espresso: $ 12.0
DarkRoast, Mocha, Mocha, Whip: $ 10.3
HouseBlend, Soy, Mocha, Whip: $ 13.3      

擴充:

現在咖啡要有區分 大,中,小杯的價格;

同理,

調料因為杯的大小也要區分不同的價格;

Beverage.java

package com.jing.decorator.size;

/**
 * 所有飲料的抽象超類
 * @author LiuJing
 *
 */
public abstract class Beverage {
  
  /**
   * 杯子的容量類型
   */
  public final static int TALL = 1;
  public final static int GRANDE = 2;
  public final static int VENTI = 3;
  
  /**
   * 杯子的容量
   */
  private int volume = 2; // 預設為中杯
  
  public int getSize() {
    return volume;
  }

  public void setSize(int valume) {
    this.volume = valume;
  }
  
  public String getSizeDescription() {
    
    String type = "未知杯的大小 ";
    switch(volume){
    case TALL:
      type = "小杯";
      break;
    case GRANDE:
      type = "中杯";
      break;
    case VENTI:
      type = "大杯";
      break;
    }

    return type;
  }

  /**
   * 飲料的名字
   */
  String description = "Unknow Beverage";
  

  
  /*
   * 擷取飲料的名字
   */
  public String getDescription(){
    return description;
  }
  
  
  /***
   * 擷取飲料的價格
   * @return 傳回計算後的總價
   */
  public abstract double cost();
  
}      

DarkRoast.java

package com.jing.decorator.size;

import java.text.DecimalFormat;

/***
 * 深烤咖啡
 * @author LiuJing
 *
 */
public class DarkRoast extends Beverage {

  /***
   * 構造時确定名字
   */
  public DarkRoast(){
    description = "DarkRoast";
  }
  
  public DarkRoast(int volume){
    setSize(volume);
    description = "DarkRoast";
  }
  
  /***
   * 價格
   */
  public double cost() {
    double cost = 0.0;
    if (getSize() == Beverage.TALL) {
      cost = 10.0;
    }else if (getSize() == Beverage.GRANDE){
      cost = 15.0;
    }else if (getSize() == Beverage.VENTI){
      cost = 20.0;
    }
    
    DecimalFormat df = new DecimalFormat("#.00");
    return Double.valueOf(df.format(cost));
  }

}      

Decaf.java

package com.jing.decorator.size;

import java.text.DecimalFormat;

/***
 * 低咖啡因咖啡
 * @author LiuJing
 *
 */
public class Decaf extends Beverage {

  /***
   * 構造時确定名字
   */
  public Decaf() {
    description = "Decaf";
  }
  public Decaf(int volume){
    setSize(volume);
    description = "Decaf";
  }
  
  /***
   * 價格
   */
  public double cost() {
    double cost = 0.0;
    if (getSize() == Beverage.TALL) {
      cost = 11.0;
    }else if (getSize() == Beverage.GRANDE){
      cost = 16.0;
    }else if (getSize() == Beverage.VENTI){
      cost = 21.0;
    }
    
    DecimalFormat df = new DecimalFormat("#.00");
    return Double.valueOf(df.format(cost));
  }

}      

Espresso.java

package com.jing.decorator.size;

import java.text.DecimalFormat;

/***
 * 濃咖啡
 * @author LiuJing
 *
 */
public class Espresso extends Beverage {

  /***
   *  構造時确定其名字
   */
  public Espresso(){
    description = "Espresso";
  }
  
  public Espresso(int volume){
    setSize(volume);
    description = "Espresso";
  }
  
  /***
   * 方法傳回 該飲料的價格
   */
  public double cost() {
    double cost = 0.0;
    if (getSize() == Beverage.TALL) {
      cost = 12.0;
    }else if (getSize() == Beverage.GRANDE){
      cost = 17.0;
    }else if (getSize() == Beverage.VENTI){
      cost = 22.0;
    }
    
    DecimalFormat df = new DecimalFormat("#.00");
    return Double.valueOf(df.format(cost));
  }
}      

HouseBlend.java

package com.jing.decorator.size;

import java.text.DecimalFormat;

/***
 * 家庭混合咖啡
 * @author LiuJing
 *
 */
public class HouseBlend extends Beverage {

  /***
   * 構造時确定其名字
   */
  public HouseBlend(){
    description = "HouseBlend";
  }
  
  public HouseBlend(int volume){
    setSize(volume);
    description = "HouseBlend";
  }
  
  /***
   * 傳回該咖啡的價格
   */
  public double cost() {
    double cost = 0.0;
    if (getSize() == Beverage.TALL) {
      cost = 13.0;
    }else if (getSize() == Beverage.GRANDE){
      cost = 18.0;
    }else if (getSize() == Beverage.VENTI){
      cost = 23.0;
    }
    
    DecimalFormat df = new DecimalFormat("#.00");
    return Double.valueOf(df.format(cost));
  }

}      

CondimentDecorator.java

package com.jing.decorator.size;

/***
 * 調料裝飾者抽象超類,繼承至 Beverage,便于類型的統一,能彼此替換。
 * @author LiuJing
 *
 */
public abstract class CondimentDecorator extends Beverage {
  
  /***
   * 要裝飾的咖啡對象
   */
  Beverage beverage;
  
  
  /***
   * 所有子類都必須重新實作 getDescription()方法
   */
  public abstract String getDescription();

}      

Milk.java

package com.jing.decorator.size;

import java.text.DecimalFormat;

/***
 * 咖啡調料 牛奶  Milk >> CondimentDecorator >> Beverage
 * @author LiuJing
 *
 */
public class Milk extends CondimentDecorator {


  /**
   * 構造時确定要加給誰
   * @param beverage
   */
  public Milk(Beverage beverage){
    this.beverage = beverage;
    setSize(beverage.getSize());
  }
  
  public int getSize(){
    return beverage.getSize();
  }
  
  /***
   * 不僅顯示目前名,還說明被裝飾的名字 
   */
  @Override
  public String getDescription() {
    // TODO Auto-generated method stub
    return beverage.getDescription() + ", Milk";
  }

  /***
   * 擷取本身的價格和咖啡的價格
   */
  @Override
  public double cost() {
    // TODO Auto-generated method stub
    double cost = beverage.cost();
    if (getSize() == Beverage.TALL){
      cost += 0.1;
    } else if (getSize() == Beverage.GRANDE){
      cost += 0.2;
    } else if (getSize() == Beverage.VENTI){
      cost += 0.3;
    }
    
    DecimalFormat df = new DecimalFormat("#.00");
    return Double.valueOf(df.format(cost));
  }

}      

Mocha.java

package com.jing.decorator.size;

import java.text.DecimalFormat;

/***
 * 咖啡調料 摩卡 Mocha >> CondimentDecorator >> Beverage
 * @author LiuJing
 *
 */
public class Mocha extends CondimentDecorator {

  
  /**
   * 構造時确定要加給誰
   * @param beverage
   */
  public Mocha(Beverage beverage){
    this.beverage = beverage;
    setSize(beverage.getSize());
  }
  
  public int getSize(){
    return beverage.getSize();
  }
  
  /***
   * 不僅顯示目前名,還說明被裝飾的名字 
   */
  @Override
  public String getDescription() {
    // TODO Auto-generated method stub
    return beverage.getDescription() + ", Mocha";
  }

  /***
   * 擷取本身的價格和咖啡的價格
   */
  @Override
  public double cost() {
    // TODO Auto-generated method stub
    double cost = beverage.cost();
    if (getSize() == Beverage.TALL){
      cost += 0.1;
    } else if (getSize() == Beverage.GRANDE){
      cost += 0.2;
    } else if (getSize() == Beverage.VENTI){
      cost += 0.3;
    }
    
    DecimalFormat df = new DecimalFormat("#.00");
    return Double.valueOf(df.format(cost));
  }

}      

Soy.java

package com.jing.decorator.size;

import java.text.DecimalFormat;

/***
 * 咖啡調料 豆漿  Soy >> CondimentDecorator >> Beverage
 * @author LiuJing
 *
 */
public class Soy extends CondimentDecorator {

  
  /**
   * 構造時确定要加給誰
   * @param beverage
   */
  public Soy(Beverage beverage){
    this.beverage = beverage;
    setSize(beverage.getSize());
  }
  
  public int getSize(){
    return beverage.getSize();
  }
  
  /***
   * 不僅顯示目前名,還說明被裝飾的名字 
   */
  @Override
  public String getDescription() {
    // TODO Auto-generated method stub
    return beverage.getDescription() + ", Soy";
  }

  /***
   * 擷取本身的價格和咖啡的價格
   */
  @Override
  public double cost() {
    // TODO Auto-generated method stub
    double cost = beverage.cost();
    if (getSize() == Beverage.TALL){
      cost += 0.1;
    } else if (getSize() == Beverage.GRANDE){
      cost += 0.2;
    } else if (getSize() == Beverage.VENTI){
      cost += 0.3;
    }
    
    DecimalFormat df = new DecimalFormat("#.00");
    return Double.valueOf(df.format(cost));
  }

}      

Whip.java

package com.jing.decorator.size;

import java.text.DecimalFormat;

/***
 * 咖啡調料 奶泡  Whip >> CondimentDecorator >> Beverage
 * @author LiuJing
 *
 */
public class Whip extends CondimentDecorator {


  /**
   * 構造時确定要加給誰
   * @param beverage
   */
  public Whip(Beverage beverage){
    this.beverage = beverage;
    setSize(beverage.getSize());
  }
  
  public int getSize(){
    return beverage.getSize();
  }
  
  /***
   * 不僅顯示目前名,還說明被裝飾的名字 
   */
  @Override
  public String getDescription() {
    // TODO Auto-generated method stub
    return beverage.getDescription() + ", Whip";
  }

  /***
   * 擷取本身的價格和咖啡的價格
   */
  @Override
  public double cost() {
    // TODO Auto-generated method stub
    double cost = beverage.cost();
    if (getSize() == Beverage.TALL){
      cost += 0.1;
    } else if (getSize() == Beverage.GRANDE){
      cost += 0.2;
    } else if (getSize() == Beverage.VENTI){
      cost += 0.3;
    }
    
    DecimalFormat df = new DecimalFormat("#.00");
    return Double.valueOf(df.format(cost));
  }

}      

Test.java

package com.jing.decorator.size;

public class Test {

  /**
   * @param args
   */
  public static void main(String[] args) {
    // TODO Auto-generated method stub

    
    // 點了一份濃咖啡
    Beverage beverage = new Espresso(); // 使用預設中杯
    System.out.println(beverage.getSizeDescription()
        + beverage.getDescription() + ": $ " + beverage.cost());

    // 點了一份深烤
    Beverage beverage2 = new DarkRoast();// 預設為中号
    beverage2.setSize(Beverage.TALL);    // 重新設定杯号為小号
    beverage2 = new Mocha(beverage2);    // 加 摩卡裝飾
    beverage2 = new Mocha(beverage2);    // 再加 摩卡裝飾
    beverage2 = new Whip(beverage2);     // 再加 奶泡裝飾
    System.out.println(beverage2.getSizeDescription()
        + beverage2.getDescription() + ": $ " + beverage2.cost());

    // 點了一份家庭混合
    Beverage beverage3 = new HouseBlend(Beverage.VENTI); // 構造初始時就設為大号
    beverage3 = new Soy(beverage3);   // 加 豆漿裝飾
    beverage3 = new Mocha(beverage3); // 再加 摩卡裝飾
    beverage3 = new Whip(beverage3);  // 再加 奶泡裝飾
    System.out.println(beverage3.getSizeDescription()
        + beverage3.getDescription()
        + ": $ " + beverage3.cost());

    // 用 工廠模式 和 生成器模式,可以更好的建立被 裝飾者對象
  }

}      
中杯Espresso: $ 17.0
小杯DarkRoast, Mocha, Mocha, Whip: $ 10.3
大杯HouseBlend, Soy, Mocha, Whip: $ 23.9