13/24 设计模式之装饰器模式 Decorator Pattern

437 阅读2分钟

类别:结构型设计模式

目的:在不改变对象对外接口的限定下,动态对对象的行为(方法)进行一次/多次增强

完整代码参考:1drv.ms/u/s!AquRvPz…

典型场景

蛋糕上需要添加一些装饰,这里假设蛋糕10块钱,每个装饰品的添加都需要支付一些额外的费用,比如添加添加糖果需要加2块钱,添加蜡烛需要1.5,既要加糖果、又要蜡烛就要多支付3.5元(2+1.5)

用代码表示这些不同状态的蛋糕很容易想到使用class表示,一个class表示一种蛋糕:

class 作用
BaseCake.java 基本蛋糕 10块
CakeWithCandy.java 加糖果的蛋糕 10+2=12块
CakeWithCandle.java 加蜡烛的蛋糕 10+1.5=11.5块
CakeWithCandyAndCandle.java 加糖果和蜡烛的蛋糕 10+2+1.5=13.5块

代码参考如下:

public interface Cake {
    abstract double getCost();
}

public class BaseCake implements Cake {
    @Override
    public double getCost() {
        return 10;
    }
}

public class CakeWithCandy implements Cake {
    @Override
    public double getCost() {
        return 12;
    }
}

public class CakeWithCandle implements Cake {
    @Override
    public double getCost() {
        return 11.5;
    }
}

public class CakeWithCandyAndCandle implements Cake {
    @Override
    public double getCost() {
        return 13.5;
    }
}

基本事实

蛋糕的装饰品之间可以任意组合(比如可以选加其中几个装饰品或者都加),在蛋糕的装饰品很少,比如两个,枚举蛋糕的装饰品是ok的,比如上面的代码中有2个装饰品,组合结果产生3个class 但是如果装饰品数量有多个,由这个装饰产生的class组合的数量就会剧增,比如再增加一个奶酪装饰品达到3个装饰品,对应的class数量将会骤增到6个

x
a b c

xa xb xc
xab xac xbc
xabc

不同装饰的蛋糕是在基础蛋糕上应用不同的行为,比如这里是价格的修改,这种情况就可以使用装饰模式实现了

模式实现

新增一个抽象装饰持有一个蛋糕对象的引用用来动态给蛋糕添加装饰,参考 Decoration.java

public abstract class Decoration implements Cake{
    private Cake cake;
    
    public Decoration(Cake cake) {
        this.cake = cake;
    }
}

给蛋糕装饰糖果和蜡烛

class CandyDecoration extends Decoration{
    public CandyDecoration(Cake cake) {
        super(cake);
    }
    
    @Override
    public double getCost () {
        return cake.getCost() + 2;
    }
}


class CandleDecoration extends Decoration{
    public CandleDecoration(Cake cake) {
        super(cake);
    }

    @Override
    public double getCost () {
        return cake.getCost() + 1.5;
    }
}

然后由使用方在程序执行期间选择是否使用某个装饰

var cake = new BaseCake();
System.out.println("base cake: " + cake.getCost());
var cakeWithCandy = new CandyDecoration(cake);
System.out.println("cake with candy: " + cakeWithCandy.getCost());
var cakeWithCandle = new CandleDecoration(cake);
System.out.println("cake with candle: " + cakeWithCandle.getCost());
var cakeWithCandyAndCandle = new CandleDecoration(new CandyDecoration(cake));
System.out.println("cake with candy and candle: " + cakeWithCandyAndCandle.getCost());

执行效果如下:

-w535

为什么装饰模式更好

动态给对象增加多个可选的行为,减少用class来描述产生的大量组合的class

UML

-w562

一些注意的点

装饰使用组合具体的Cake对象来实现

参考资料

  1. www.geeksforgeeks.org/decorator-p…