Design Pattern:Decorator Pattern 說明

主要概念為 Client 只需要 reference Thing object,可以有多種實作方式,並且定義 Decorator 包裝每一個SpecificThiing 的內容。同時,也可以衍生 SpecifiecDecorator 定義特定要包裝的介面,其他的介面就直接透過 Decorator delegate to SpecificThiing,或者做統一的包裝。

 

透過 Decorator 將每一個特定條件記錄在特定的 class 內,減少 if 判斷,增加理解能力。

舉例如下:

我們有一個 Hat class 定義:

public interface Hat {
    public String getName();
    public int getPrice();
    public String getDescription();
    public boolean isPremium();
}

主要商品為 StandardHat & PreminumHat

public class StandardHat implements Hat {
    private String name;
    public StandardHat(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
    public int getPrice() {
        return 2000;
    }
    public String getDescription() {
        return getName();
    }
    public boolean isPremium() {
        return false;
    }
}
public class PremiumHat implements Hat {
    private String name;
    public PremiumHat(String name) {
        this.name = name;
    }
 
    public String getName() {
        return name;
    }
 
    public int getPrice() {
        return 3000;
    }
   
    public String getDescription() {
        return getName();
    }
   
    public boolean isPremium() {
        return true;
    }
}

定義 DecoratorHat 處理所有 Decorator 共通的項目(例如回傳名稱):

public abstract class DecoratedHat implements Hat {
 
    private Hat decoratedHat;
 
    protected DecoratedHat(Hat decoratedHat) {
        this.decoratedHat = decoratedHat;
    }
 
    public String getName() {
        return decoratedHat.getName();
    }
 
    protected Hat getDecoratedHat() {
        return decoratedHat;
    }
}

透過 RibbonHat 可以在 Hat 上加裝緞帶:

public class RibbonedHat extends DecoratedHat {
      
    public RibbonedHat(Hat decoratedHat) {
        super(decoratedHat);
    }
   
    public int getPrice() {
        return getDecoratedHat().getPrice() + 100;
    }
   
    public String getDescription() {
        return getDecoratedHat().getDescription() + ", ribboned";
    }
   
    public boolean isPremium() {
        return getDecoratedHat().isPremium();
    }
}

GoldDecorator 塗上金色:

public class GoldenHat extends DecoratedHat {
      
    public GoldenHat(Hat decoratedHat) {
        super(decoratedHat);
    }
   
    public int getPrice() {
        if (isPremium()) {
            return getDecoratedHat().getPrice() + 1000;
        } else {
            return getDecoratedHat().getPrice() + 200;
        }
    }
   
    public String getDescription() {
        return "golden " + getDecoratedHat().getDescription();
    }
   
    public boolean isPremium() {
        return getDecoratedHat().isPremium();
    }
}

Decorator 的使用方式如下:

public void testGoldenRibbonedStandardHat() {
    Hat hat = new RibbonedHat(new GoldenHat(new StandardHat("baseball cap")));
        
    assertThat(hat.getPrice(), is(2300));
    assertThat(hat.getDescription(), is("golden baseball cap, ribboned"));
}

 

Design Pattern:Adapter Pattern 說明

主要概念:

Client 要使用 CompatibleThing object,但實際的物件介面沒有繼承,因此必須要透過 Adapter 包裝 IncompatibleThing,讓他可以有這樣的介面可以讓 Client 引用。

舉例而言:ShoppingCart 使用 item 項目結帳,但如果我們要直接使用 Hat object 該如何處理?

程式碼如下:

Item Class:

public abstract class Item {
    public abstract String itemName();
    public abstract int itemPrice();
}

Hat Class:

public class Hat { 
    private static final double VAT_PERCENT = 0.2;
    private final String shortName;
    private final String longName;
   
    private final int basePrice;
   
    public Hat(String shortName, String longName, int price) {
        this.shortName = shortName;
        this.longName = longName;
        this.basePrice = price;
    }
   
    public String getShortName() {
        return shortName;
    }
   
    public String getLongName() {
        return longName;
    }
   
    public int getPrice() {
        return (int) (basePrice * (1 + VAT_PERCENT));
    }
}

ShoppingCart Class:

public class ShoppingCart {
   
    private List<Item> items = new LinkedList<>();
   
    public void add(Item item) {
        items.add(item);
    }
   
    public boolean remove(Item item) {
        return items.remove(item);
    }
   
    public long getTotalPrice() {
        long result = 0;
        for (Item item: items) {
            result += item.itemPrice();
        }
        return result;
    }
   
    public String getReceipt() {
        StringBuffer result = new StringBuffer();
        for (Item item: items) {
            result.append(item.itemName() + "\t" + formatMoney(item.itemPrice()) + "\n");
        }
        result.append("----------------------\n");
        result.append("Total:\t\t" + formatMoney(getTotalPrice()) + "\n");
        return result.toString();
    }
   
    private String formatMoney(long money) {
        return "€" + (money / 100) + "." + (money % 100);
    }
}

可以看到 ShoppingCart 只接受 Item object,因此除非 Hat 也要實作 Item 介面,否則不可能傳入到 shoppingCart 中。

解決方案就是建立 Adapter,繼承 Item reference Hat 將他 wrap :

public class HatAdapter extends Item {
   
    private Hat hat;
   
    public HatAdapter(Hat hat) {
        super();
        this.hat = hat;
    }
   
    @Override
    public String itemName() {
        return hat.getShortName();
    }
   
    @Override
    public int itemPrice() {
        return hat.getPrice();
    }
}

如此,就可以將 Hat 傳入到 Adapter ,讓 ShoppingCart 可以直接使用:

public static void main(String[] args) {
     HatAdapter goldenHat = new HatAdapter(new Hat("Golden hat", "Golden hat", 4999));
     HatAdapter pointyHat = new HatAdapter(new Hat("Pointy hat", "Pointy hat", 3000));
     HatAdapter purpleHat = new HatAdapter(new Hat("Purple hat", "Purple hat", 1490));
   
     ShoppingCart cart = new ShoppingCart();
          
     cart.add(goldenHat);
     cart.add(pointyHat);
     cart.add(purpleHat);
          
     cart.remove(pointyHat);
          
     System.out.println(cart.getReceipt());
}

Pattern 架構如下: