Fork me on GitHub

代理模式和装饰者模式傻傻分不清?

代理模式和装饰者模式是两种非常常用的设计模式,两者都可以用来扩展对象的行为,因此把这两种设计模式放到一起讨论。
从下图可以发现这种设计模式的类图非常的相似(装饰者模式省略了一些实现)


下面我们分别讨论一下这两种模式,然后再比较他们之间的异同点。

代理模式

首先我们谈代理模式,正如它的名称所示,它为调用方提供一个接口的代理,同时向调用方屏蔽代理的细节,调用方是不清楚这个代理后面究竟是什么,这么做的目的也是显而易见的,代理可以控制被代理接口的行为而不被调用方感知,很多Java框架代码中都能看见代理模式的身影。
现实中也有很多类似代理模式的例子,比如我们去找中介租房的时候,房屋中介就可以看做是房东的代理,这里房东和房屋中介都可以看做是出租者,而我们在租房是往往是不清楚房东的具体情况的(这里点是很重要的)。下面我们可以用代码模拟这种情况。

  1. 出租者接口

    1
    2
    3
    4
    // 出租者接口
    public interface Lessor<T> {
    T lease();
    }
  2. 房东类实现Lessor接口

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    @Slf4j
    public class Landlord implements Lessor<House> {

    public House lease() {
    double sent = 5000.00d;
    log.info("出租一套两居室,一个月收{}块吧,嫌麻烦,直接找个中介吧", sent);
    return new House().setRent(sent);
    }

    }
  3. 房屋中介类实现Lessor接口,并且关联一个房东,注意这里并不一定是通过这种组合的方式关联被代理类。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    @Slf4j
    public class LettingAgency implements Lessor<House> {
    private Lessor<House> landlord = new Landlord();

    public House lease() {
    log.info("我是XXX中介,把房子交给我吧,很快就能租出去。");
    House house = landlord.lease();
    house.setRent(house.getRent() * 1.5d);
    log.info("加点辛苦费吧");
    return house;
    }
    }
  4. 承租人找中介租房

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    @Slf4j
    public class Lessee {

    public static void main(String[] args) {
    Lessor<House> lessor = new LettingAgency();
    House house = lessor.lease();
    log.info("租到了个两居室,花了{}块钱", house.getRent());
    }

    }

从上面的代码,我们可以发现代理模式的几个关键点

  1. 代理类(中介)和被代理类(房东)都实现了相同的接口(出租者)
  2. 代理类可以在被代理类的基础上做一些额外的控制(加中介费)
  3. 调用方不清楚被代理类的细节(这里承租人只知道租了中介的7500块的房子)

装饰者模式

接下来我们再谈谈装饰者模式,装饰者模式提供一种比继承更加灵活的扩展对象的方法,完全遵循开闭原则
从类图上看,装饰者模式和代理模式非常相似。但是装饰者模式和代理模式不同的地方在于,装饰者模式更加着重于增加被装饰者的行为。下面我们用一个例子来展示装饰者模式可以做什么,这个例子展示如何调制一杯心仪的饮料

  1. 首先,我们有个饮料接口

    1
    2
    3
    4
    public interface Beverage {
    // 调制饮料
    void concoct();
    }
  2. 浓缩咖啡实现了饮料接口

    1
    2
    3
    4
    5
    6
    @Slf4j
    public class Espresso implements Beverage {
    public void concoct() {
    log.info("添加浓缩咖啡");
    }
    }
  3. 创建一个抽象装饰器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    public abstract class BeverageDecorator implements Beverage {
    private Beverage beverage;

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

    protected abstract void prepare();
    protected abstract void process();

    @Override
    public void concoct() {
    prepare();
    // 在调用beverage之前可以选择做一下准备工作
    beverage.concoct();
    // 在完成beverage之后也可以选择做一些处理
    process();
    }
    }
  4. 创建一个拿铁咖啡,它实现了BeverageDecorator

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    @Slf4j
    public class Latte extends BeverageDecorator {
    public Latte(Beverage beverage) {
    super(beverage);
    }

    @Override
    protected void prepare() {
    log.info("先打一杯热牛奶");
    }

    @Override
    protected void process() {
    log.info("添加大量牛奶");
    }
    }
  5. 可以在创建一个焦糖拿铁

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    @Slf4j
    public class CaramelLatte extends BeverageDecorator {
    public CaramelLatte(Beverage beverage) {
    super(beverage);
    }

    @Override
    protected void prepare() {
    }

    @Override
    protected void process() {
    log.info("添加一些焦糖");
    }
    }
  6. 看一看调用方如何使用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public class Barista {
    public static void main(String[] args) {
    // 我需要一杯原味拿铁
    Espresso espresso = new Espresso();
    Latte latte = new Latte(espresso);
    // 不太合口味,加点焦糖吧
    CaramelLatte caramelLatte = new CaramelLatte(latte);
    // 开始调制
    caramelLatte.concoct();
    }
    }

从上面的代码,我们总结装饰者模式的几个关键点:

  1. 装饰者和被装饰者实现同一接口,这点和代理模式一样
  2. 装饰者可以为被装饰者添加额外的行为(添加原料),这点和代理模式类似,但是目的不同
  3. 被装饰者是调用方给装饰者的,装饰者只负责添加额外的行为,这点和代理模式完全不同
  4. 被装饰者可以被多个装饰者装饰,从而被赋予多个额外的行为

总结

通过上述的代码实例,我们很容易发现,代理模式和装饰者模式之间的差异,虽然具有相似的代码结构,也都可以在目标对象的方法调用前后增加行为,但是实现的目的是有区别的:装饰者模式提供一种灵活的扩展对象的方式,被装饰对象一般是由调用方提供的,而代理模式主要是为了控制对对象的访问,被代理对象相对于调用方来说是透明的。
我们会经常在一些Java框架代码中发现代理模式的另一种实现方式————动态代理,关于动态代理的一些实现方式,我们可以在另外的文章中讨论。

本文标题:代理模式和装饰者模式傻傻分不清?

文章作者:山坡杨

发布时间:2019年03月24日 - 14:49:38

最后更新:2019年03月26日 - 00:41:23

原始链接:http://www.yangxf.top/4/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

感觉本站内容不错,读后有收获?
0%