【设计模式】装饰器模式

Alan 313 2019-06-03
定义

装饰器模式也称为包装模式,是指在不改变原有对象的基础上,将功能附加到原有对象上,提供了比继承更有弹性的替代方案(扩展原有对象的功能),属于结构型模式。

举例

这里举例一个日常生活常见的例子。我们每天吃早饭可能会买的煎饼果子。煎饼果子是可以加上调料和其他肉食的。如果觉得不够吃,可以加上鸡蛋或者香肠。

  1. 创建煎饼果子类。提供getName和getPrice方法。
public class BatterCake {

    protected String getName(){
        return "煎饼";
    }

    protected int getPrice(){
        return 5;
    }
}
  1. 觉得不够吃,想加一个鸡蛋。创建一个类BattarCakeWithOneEgg继承BatterCake类,重写getName和getPrice方法。
public class BattarCakeWithOneEgg extends BatterCake {

    @Override
    protected String getName() {
        return super.getName() + "加一个鸡蛋";
    }

    @Override
    protected int getPrice() {
        return super.getPrice() + 1;
    }
}
  1. 还是觉得不够吃,想再加一个香肠。创建一个类继续继承BattarCakeWithOneEgg,重写getName和getPrice方法。
public class BatterCakeWithOneEggOneSausage extends BattarCakeWithOneEgg {

    @Override
    protected String getName() {
        return super.getName() + "加一个香肠";
    }

    @Override
    protected int getPrice() {
        return super.getPrice() + 2;
    }
}
  1. 测试代码。
public class Test {
    public static void main(String[] args) {
        //想吃煎饼果子
        BatterCake batterCake = new BatterCake();
        System.out.println(batterCake.getName() + ",价格:" + batterCake.getPrice());

        //想再加一个鸡蛋
        BattarCakeWithOneEgg battarCakeWithOneEgg = new BattarCakeWithOneEgg();
        System.out.println(battarCakeWithOneEgg.getName() + ",价格:" + battarCakeWithOneEgg.getPrice());

        //想再加一个鸡蛋一个香肠
        BatterCakeWithOneEggOneSausage batterCakeWithOneEggOneSausage = new BatterCakeWithOneEggOneSausage();
        System.out.println(batterCakeWithOneEggOneSausage.getName() + ",价格:" + batterCakeWithOneEggOneSausage.getPrice());
    }
}
  1. 看看结果。

这个时候,如果我想再一个鸡蛋。就需要继续继承BatterCakeWithOneEggOneSausage 这个类。想再加,就必须再继承。

这样不停的定制,显然是不科学的做法。

下面就用装饰器模式来改造下这个代码。

  1. 创建一个抽象的煎饼对象,提供getName和getPrice方法。
public abstract class BatterCake {
    public abstract String getName();
    public abstract int getPrice();
}
  1. 创建一个基础的煎饼果子类(基础套餐)
public class BaseBatterCake extends BatterCake {
    @Override
    public String getName() {
        return "煎饼";
    }

    @Override
    public int getPrice() {
        return 5;
    }
}
  1. 创建一个煎饼果子基础套餐的装饰器,将抽象的BatterCake类以静态代理的方式注入到BatterCakeDecorator 的构造方法中,然后调用它的getName和getPrice的方法。
public class BatterCakeDecorator extends BatterCake {

    private BatterCake batterCake;

    public BatterCakeDecorator(BatterCake batterCake) {
        this.batterCake = batterCake;
    }

    @Override
    public String getName() {
        return batterCake.getName();
    }

    @Override
    public int getPrice() {
        return batterCake.getPrice();
    }
}
  1. 创建一个加了鸡蛋的煎饼果子套餐,继承BatterCake,在构造方法中注入BatterCake,然后重写getName和getPrice的方法。
public class BatterCakeEggDecorator extends BatterCakeDecorator {

    public BatterCakeEggDecorator(BatterCake batterCake) {
        super(batterCake);
    }

    @Override
    public String getName() {
        return super.getName() + "加一个鸡蛋";
    }

    @Override
    public int getPrice() {
        return super.getPrice() + 1;
    }
}
  1. 创建一个加香肠的煎饼果子套餐,同样继承BatterCake,在构造方法中注入BatterCake,然后重写getName和getPrice的方法。
public class BatterCakeSausageDecorator extends BatterCakeDecorator {
    public BatterCakeSausageDecorator(BatterCake batterCake) {
        super(batterCake);
    }

    @Override
    public String getName() {
        return super.getName() + "加一个香肠";
    }

    @Override
    public int getPrice() {
        return super.getPrice() + 2;
    }
}
  1. 测试。
public class Test {
    public static void main(String[] args) {
        BatterCake batterCake;
        //点一个基础套餐
        batterCake = new BaseBatterCake();
        //加一个鸡蛋
        batterCake = new BatterCakeEggDecorator(batterCake);
        //加一个香肠
        batterCake = new BatterCakeSausageDecorator(batterCake);
        //再加一个香肠
        batterCake = new BatterCakeSausageDecorator(batterCake);

        System.out.println("买了" + batterCake.getName() + ",一共花了" + batterCake.getPrice() + "元钱");
    }
}
  1. 查看结果。

  2. 看一下类图。

这里的BatterCake就是一个抽象的组件,它充当了被装饰的原始对象,规定了被装饰对象的行为。BaseBatterCake就是具体组件,他是实现或者继承抽象组件的一个具体对象,也就是被装饰对象。BatterCakeDecorator就是通用的装饰器,它的内部必然有一个属性指向抽象的组件。它的实现一般是一个抽象类,主要是为了让它的子类按照其构造形式传入一个抽象的组件,这是强制的通用行为。而如果系统中的装饰逻辑比较的单一,并不需要许多的装饰器,那么就可以直接省略这个类,直接实现一个具体的装饰器就可以。而下面的 BatterCakeEggDecorator 和 BatterCakeSausageDecorator 就是具体的装饰器对象。理论上,每一个对象都扩展了BaseBatterCake的功能。

总结

装饰器模式角色分配符合设计模式的里氏替换原则,依赖倒置原则,从而使得它具备很强的扩展性,最终满足开闭原则。

  1. 装饰器模式和代理模式的对比

装饰器更强调对被装饰对象自身的扩展,而代理模式更强调代理对象的功能增强。

  1. 代理模式的优点

​ (1).是继承的有力补充,比继承灵活,不改变原有对象的情况下动态地给一个对象扩展功能,即插即用。

​ (2).可以通过构造不同的装饰类,对这些类进行排列组合,实现不同的效果。

​ (3).装饰器模式完全遵循开闭原则。

  1. 代理模式的缺点

​ (1).会出现很多的代码,出现更多的类,增加了系统的复杂度。

​ (2).动态装饰时,多层装饰会比较复杂。


# 设计模式