设计模式学习笔记(4):装饰者模式

       在之前面对系统扩展的方式,我们往往会选用继承的方式,在参数多的时候往往会造成子类数量的爆炸。如何在运行时动态扩展呢,这个时候装饰者模式应运而生。

       一、装饰者模式的定义:

       装饰模式是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。

       二、 应用场景:

       1.需要扩展一个类的功能,或给一个类添加附加职责。
       2.需要动态的给一个对象添加功能,这些功能可以再动态的撤销。
       3.需要增加由一些基本功能的排列组合而产生的非常大量的功能,从而使继承关系变的不现实。

       4.当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类。

       直白点说:装饰者模式使用类似于“什么”的“什么”的“什么”,这种情况下,形容词往往是可以动态增加的。

       三、优点:

       1. Decorator模式与继承关系的目的都是要扩展对象的功能,但是Decorator可以提供比继承更多的灵活性。

       2. 通过使用不同的具体装饰类以及这些装饰类的排列组合,设计师可以创造出很多不同行为的组合。

       四、缺点:

       1. 这种比继承更加灵活机动的特性,也同时意味着更加多的复杂性。
       2. 装饰模式会导致设计中出现许多小类,如果过度使用,会使程序变得很复杂。

       3. 装饰模式是针对抽象组件(Component)类型编程。但是,如果你要针对具体组件编程时,就应该重新思考你的应用架构,以及装饰者是否合适。当然也可以改变Component接口,增加新的公开的行为,实现“半透明”的装饰者模式。在实际项目中要做出最佳选择。

       五、设计原则:

       类应该对外扩展开放,对修改关闭

       多用组合,少用继承

       

示例:

       一家咖啡店设计了一个系统,用于售卖咖啡产品,展示咖啡价格。咖啡品种有四种:HouseBlend、DarkRoast、Decaf以及Espresso,并且需要我们添加调味品,分别有:Milk,Soy,Mocha,Whip。以咖啡为主体,可以加入多种调味品。如果我们采用继承的方式去设计系统,子类会有HouseBlendWithMilkAndSoyAndMocha,DarkRoastWithWhip,可见子类会呈现排列组合式的增加,这样会给维护和后期扩展(若是价格变动)造成巨大影响。

       换一种设计方式,我们将调味料作为Beverage里面的变量,这么设计父类,具体品种继承父类。

       


       这是比较常见的一种设计方法。但是呢,如果后期添加删除调料,在扩展的时候我们还是要对这个类进行修改。有没有方法能够实现动态的增加,并且在扩展的时候不改动已有的类呢。

       

        可见所有的组件全都继承了Beverage这个超类,为什么如此做呢?我们利用继承达到“类型匹配”,而不是用继承获得“行为”。我们这里的Beverage是一个抽象类(其实也可以用接口实现,但是考虑到抽象类中可以有方法体,这样我们咖啡中就不用重写其他方法了)。这种类型匹配主要作用于调料类的相互调用和调用咖啡类,这样实现就可以实现我们动态的扩展了。

Beverage类:

public abstract class Beverage {
    String description = "Unknown Beverage";

    public String getDescription(){
        return description;
    }

    public abstract double cost();
}

CondimentDecorator类:

public abstract class CondimentDecorator extends Beverage{
    @Override
    public abstract String getDescription();
}

咖啡类Espresso:

public class Espresso extends Beverage {
    
    public Espresso(){
        description = "Espresso";
    }

    //Espresso价格1.99
    @Override
    public double cost() {
        return 1.99;
    }
}

咖啡类HouseBlend:

public class HouseBlend extends Beverage {

    public HouseBlend() {
        description = "House Blend Coffee";
    }

    //HouseBlend
    @Override
    public double cost() {
        return 0.89;
    }
}
咖啡类DarkRoast:
public class DarkRoast extends Beverage {

    public DarkRoast() {
        description = "DarkRoast";
    }

    //DarkRoast价格0,99
    @Override
    public double cost() {
        return 0.99;
    }
}
咖啡类Decaf:
public class Decaf extends Beverage {

    public Decaf() {
        description = "Decaf";
    }

    //Decaf价格1.05
    @Override
    public double cost() {
        return 1.05;
    }
}

调料类Whip:

public class Whip extends CondimentDecorator {
    
    //方便实现引用其他Beverage类,然后把其作为构造器的参数
    Beverage beverage;
    
    public Whip(Beverage beverage) {
        this.beverage = beverage;
    }

    @Override
    public String getDescription() {
        return beverage.getDescription() + ",Whip";
    }

    @Override
    public double cost() {
        return 0.1 + beverage.cost();
    }
}
调料类Mocha:
public class Mocha extends CondimentDecorator {

    Beverage beverage;

    public Mocha(Beverage beverage) {
        this.beverage = beverage;
    }

    @Override
    public String getDescription() {
        return beverage.getDescription() + ",Mocha";
    }

    @Override
    public double cost() {
        return 0.2 + beverage.cost();
    }
}
调料类Soy:

public class Soy extends CondimentDecorator {

    Beverage beverage;

    public Soy(Beverage beverage) {
        this.beverage = beverage;
    }

    @Override
    public String getDescription() {
        return beverage.getDescription() + ",soy";
    }

    @Override
    public double cost() {
        return 0.15 + beverage.cost();
    }
}
调料类Milk:
public class Milk extends CondimentDecorator {

    Beverage beverage;

    public Milk(Beverage beverage) {
        this.beverage = beverage;
    }

    @Override
    public String getDescription() {
        return beverage.getDescription() + ",Milk";
    }

    @Override
    public double cost() {
        return 0.10 + beverage.cost();
    }
}
测试类:
public class StarbuzzCoffee {

    public static void main(String[] args) {
        Beverage beverage = new Espresso();

        System.out.println(beverage.getDescription() + beverage.cost());

        Beverage beverage1 = new DarkRoast();
        beverage1 = new Mocha(beverage1);
        beverage1 = new Mocha(beverage1);
        beverage1 = new Whip(beverage1);
        System.out.println(beverage1.getDescription() +  beverage1.cost());

        Beverage beverage2 = new HouseBlend();
        beverage2 = new Soy(beverage2);
        beverage2 = new Mocha(beverage2);
        beverage2 = new Whip(beverage2);

        System.out.println(beverage2.getDescription() + beverage2.cost());
    }
}

结果:

但是我们也可以看到,我们类的数量得到了很大的增长。如果我们不需要进行动态扩展,其实应用第二个方法就可以。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值