翔谈设计模式——装饰者模式

设计模式之装饰者模式

最近在买奶茶的时候突发奇想,假如我要设计一个奶茶订单系统,这个奶茶价格计算要怎样设计才算方便呢?毕竟奶茶种类那么多,而且能加的配料也很多,并且奶茶店可能随时还会增加奶茶的种类及可选的配料,想要满足这些经常改变或增加的需求,怎么设计代码才是易维护、易扩展的呢?在了解了装饰者模式之后,我终于知道该怎么做了!

翔谈设计模式——装饰者模式_第1张图片

1.定义

装饰者模式(Decorator Pattern)动态地将责任附加在对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。

装饰者模式包含以下角色:

  1. 抽象构件
  2. 具体构件
  3. 抽象装饰类
  4. 具体装饰类

2.实例说明

按照大白话来讲,抽象构件就是奶茶,具体构件是珍珠奶茶、波霸奶茶、红豆奶茶这些具体的奶茶。而抽象装饰类和具体装饰类是装饰者模式的设计核心了,利用它们我们可以动态地给奶茶加上配料。

2.1 抽象奶茶类

public abstract class MilkTea {
   public abstract String getDescription();
   public abstract int cost();
}

2.2 具体奶茶类

// 波霸奶茶,一杯15块钱
public class BoBaMilkTea extends MilkTea {
    @Override
    public String getDescription() {
        return "波霸奶茶";
    }

    @Override
    public int cost() {
        return 15;
    }
}

// 红豆奶茶,一杯12块钱
public class HongDouMilkTea extends MilkTea {
    @Override
    public String getDescription() {
        return "红豆奶茶";
    }

    @Override
    public int cost() {
        return 12;
    }
}

2.3 抽象配料类

由于我们是让配料来扩展奶茶,因此需要继承抽象奶茶类并且实现里面的抽象方法。

public abstract class Decorator extends MilkTea{
    private MilkTea milkTea;

    public Decorator(MilkTea milkTea){
        this.milkTea = milkTea;
    }

    @Override
    public String getDescription() {
        return milkTea.getDescription();
    }

    @Override
    public int cost() {
        return milkTea.cost();
    }
}

2.4 具体配料类

// 加奇异果要3块钱!
public class QiYiGuoDecorator extends Decorator {
    public QiYiGuoDecorator(MilkTea milkTea){
        super(milkTea);
    }

    @Override
    public String getDescription() {
        return super.getDescription() + " 加奇异果";
    }

    @Override
    public int cost() {
        return super.cost() + 3;
    }
}


// 加椰果才要2块钱!
public class YeGuoDecorator extends Decorator {
    public YeGuoDecorator(MilkTea milkTea){
        super(milkTea);
    }

    @Override
    public String getDescription() {
        return super.getDescription() + " 加椰果";
    }

    @Override
    public int cost() {
        return super.cost() + 2;
    }
}

3.测试

测试代码和测试结果如下。

public class Test {
    public static void main(String args[]){
        //先来一杯红豆奶茶,加椰果,加奇异果!
        MilkTea hongDouMilkTea = new HongDouMilkTea();
        hongDouMilkTea = new YeGuoDecorator(hongDouMilkTea);
        hongDouMilkTea = new QiYiGuoDecorator(hongDouMilkTea);
        System.out.println(hongDouMilkTea.getDescription() + ",价格:" + hongDouMilkTea.cost() + "元");

        //再来一杯波霸奶茶,再加双倍椰果,别问我为什么!我爱椰果!
        MilkTea boBaMilkTea = new BoBaMilkTea();
        boBaMilkTea = new YeGuoDecorator(boBaMilkTea);
        boBaMilkTea = new YeGuoDecorator(boBaMilkTea);
        System.out.println(boBaMilkTea.getDescription() + ",价格:" + boBaMilkTea.cost() + "元");
    }
}

翔谈设计模式——装饰者模式_第2张图片

4.优缺点

4.1 优点

1.装饰模式与继承关系的目的都是要扩展对象的功能,但是装饰模式可以提供比继承更多的灵活性。
2.通过使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合。可以使用多个具体装饰类来装饰同一对象,得到功能更为强大的对象。
3.具体构件类与具体装饰类可以独立变化,用户可以根据需要增加新的具体构件类和具体装饰类,在使用时再对其进行组合,原有代码无须改变,符合“开闭原则”

4.2 缺点

1.使用装饰模式进行系统设计时将产生很多小对象,这些对象的区别在于它们之间相互连接的方式有所不同,而不是它们的类或者属性值有所不同,同时还将产生很多具体装饰类。这些装饰类和小对象的产生将增加系统的复杂度。
2.这种比继承更加灵活机动的特性,也同时意味着装饰模式比继承更加易于出错,排错也很困难,对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为烦琐。

5.适用场景

1.当不能采用继承的方式对系统进行扩充或者采用继承不利于系统扩展和维护时。不能采用继承的情况主要有两类:第一类是系统中存在大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长;第二类是因为类定义不能继承(如final类).
2.在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。

你可能感兴趣的:(设计模式,设计模式,装饰者模式)