裝飾器模式
裝飾器模式
雙11大拍賣
電腦顯示卡需要特價促銷
如果使用繼承來設計類別
繼承
public abstract class Goods {
public String descript;
public abstract int cost();
}
class Rtx3060 extends Goods{
@Override
public int cost() {
return 9500;
}
}
class Rtx4060 extends Goods{
@Override
public int cost() {
return 10500;
}
}
class Rtx3060_11_off extends Rtx3060{
@Override
public int cost() {
return (int) (super.cost() * 0.85);
}
}
考慮到有很多商品加上有各種的促銷,會有非常多的類別需要維護
修改基礎類別做調整
使用基礎類別來控制各種計算流程,配合繼承使子類別做出額外的計算與紀錄
public abstract class Goods {
public String descript;
protected int i_cost;
public double cost(){
double base = 1;
if (b11_off){
base *= 0.88;
}
if(b3c_off){
base *= 0.95;
}
return base;
}
protected boolean b11_off;
protected boolean b3c_off;
public boolean Is11_off(){ return b11_off;}
public void set_11_off(){};
}
class Rtx3060 extends Goods{
public Rtx3060(){
b11_off = true;
}
@Override
public double cost() {
return Math.round(super.cost() * 9500);
}
}
這樣可以正確計算,也可以減少類別數量
不過有幾個缺點
- 折扣改變必須修改程式
- 有新的促銷活動也必須加入新的折扣
- 賣化妝品的時候,化妝品並不需要3c折扣的方法
延伸解釋3的問題
使用繼承可以重複使用程式碼,重複利用的同時
也必須為每個繼承基礎類別的程式維護
全部的子類別不一定需要基礎類別的所有行為
使用裝飾器模式
- 一個商品RTX4060
- 雙11拍賣包裝RTX4060
- 3C拍賣包裝雙11
透過一層包裝一層,可以達到動態執行
呼叫的順序為 3->2->1
分為兩個種類
- 裝飾器類別
- 被裝飾類別
- 裝飾器與被裝飾類別的基礎型別都是一樣的
- 可以使用一個以上的裝飾器來裝飾
- 裝飾器可以幫被被裝飾類別加入自己的行為
裝飾器的類別圖
定義;可以動態幫物件附加行為

裝飾器繼承Component不是為了取得基礎類別的行為
是為了取得與基礎類別一樣的型態
然後就可以不用透過繼承獲得行為
透過一樣的型態的組件互相組合
能獲得不同於繼承的高度彈性(執行時可以改變)
程式碼範例
public abstract class GoodsComponent {
public abstract double cost();
}
class Rtx3060 extends GoodsComponent{
@Override
public double cost() {
return 9500;
}
}
abstract class GoodsDecorator extends GoodsComponent{
protected GoodsComponent goods;
}
class C11_off extends GoodsDecorator{
public C11_off(GoodsComponent goods){
this.goods = goods;
}
@Override
public double cost() {
return goods.cost() * 0.95;
}
}
class C3c_off extends GoodsDecorator{
public C3c_off(GoodsComponent goods){
this.goods = goods;
}
@Override
public double cost() {
return goods.cost() * 0.88;
}
}
使用方法
GoodsComponent rtx3060 = new Rtx3060();
rtx3060 = new C11_off(rtx3060);
rtx3060 = new C3c_off(rtx3060);
System.out.println(rtx3060.cost());