装饰者模式

定义

装饰者模式采用组合的方式,动态地将责任附加到对象上,若要扩展功能,装饰者提供了比继承更有弹性的替代方案。组合和委托可用于在运行时动态地添加新的行为。

装饰者模式实现

设计冰淇淋小站

炎炎夏日,我们都喜欢吃冰淇淋解暑,有各种口味,完了我还可以选择喜欢的调味酱加在冰淇淋上,我要设计一个冰淇淋的售卖系统,该怎么设计呢?这里就需要用到装饰者模式,装饰者是调味酱,被装饰者是冰淇淋
这里写图片描述
这里写图片描述
用调味酱将冰淇淋包装一层,就是说将冰淇淋对象传入调味酱对象中进行扩展,这个我可以理解,但是我不明白,为啥冰淇淋包装类IceCreamDecorator要与冰淇淋有相同的超类呢,试想一下,我买冰淇淋,加了调味酱之后,递给我的变成了一杯奶茶,这不是我想要的呀,因此装饰者和被装饰者必须是一样的类型,即有共同的超类,这样装饰者才能取代被装饰者,我们利用继承达到类型匹配,而不是利用继承获得行为。包装前是IceCream对象,包装完了还是IceCream对象,装饰者对被装饰者的客户是透明的,除非客户程序依赖于被装饰者的具体类型。

冰淇淋小站代码实现

/**
 * 超类
 * Created by zhoupengxiao on 16/6/16.
 */
public abstract class IceCream {
    //获取名称;
    public abstract String getDescription();

    //获取价格;
    public abstract double cost();
}
/**
 * 原味冰淇淋
 * Created by zhoupengxiao on 16/6/16.
 */
public class OriginalTaste extends IceCream {
    @Override
    public String getDescription() {
        return "原味";
    }

    @Override
    public double cost() {
        return 3.5;
    }
}
/**
 * 抹茶冰淇淋
 * Created by zhoupengxiao on 16/6/16.
 */
public class MatchaTaste extends IceCream {
    @Override
    public String getDescription() {
        return "抹茶味";
    }

    @Override
    public double cost() {
        return 4.0;
    }
}
/**
 * 装饰类
 * Created by sarahzhou on 16/6/16.
 */
public abstract class IceCreamDecorator extends IceCream {
}
/**
 * 蓝莓酱
 * Created by sarahzhou on 16/6/16.
 */
public class BlueberrySauce extends IceCreamDecorator {
    //被包装者
    private IceCream iceCream;

    public BlueberrySauce(IceCream iceCream) {
        this.iceCream = iceCream;
    }

    @Override
    public String getDescription() {
        return iceCream.getDescription() + " 蓝莓酱";
    }

    @Override
    public double cost() {
        return iceCream.cost() + 0.8;
    }
}
/**
 * 芒果酱
 * Created by zhoupengxiao on 16/6/16.
 */
public class MangoSauce extends IceCreamDecorator {
    //被包装者
    private IceCream iceCream;

    public MangoSauce(IceCream iceCream) {
        this.iceCream = iceCream;
    }

    @Override
    public String getDescription() {
        return iceCream.getDescription() + " 芒果酱";
    }

    @Override
    public double cost() {
        return iceCream.cost() + 0.6;
    }
}
/**
 * 巧克力酱
 * Created by sarahzhou on 16/6/16.
 */
public class ChocolateSauce extends IceCreamDecorator {
    //被包装者
    private IceCream iceCream;

    public ChocolateSauce(IceCream iceCream) {
        this.iceCream = iceCream;
    }

    //获取名称;
    @Override
    public String getDescription() {
        return iceCream.getDescription() + " 巧克力酱";
    }

    //获取价格; 0.5是巧克力酱的价格;
    @Override
    public double cost() {
        return iceCream.cost() + 0.5;
    }
}

测试类和输出结果

public class TestDecoration {
    @Test
    public void test1() {
        IceCream iceCream = new OriginalTaste();
        iceCream = new MangoSauce(iceCream);
        iceCream = new BlueberrySauce(iceCream);
        iceCream = new BlueberrySauce(iceCream);
        System.out.println(iceCream.getDescription() + " : ¥" + String.format("%.2f", iceCream.cost()));


        IceCream iceCream1 = new MatchaTaste();
        iceCream1 = new ChocolateSauce(iceCream1);
        iceCream1 = new ChocolateSauce(iceCream1);
        iceCream1 = new BlueberrySauce(iceCream1);
        iceCream1 = new MangoSauce(iceCream1);
        System.out.println(iceCream1.getDescription() + " : ¥" + String.format("%.2f", iceCream1.cost()));
    }
}

这里写图片描述

如何理解组合和委托?

组合Composition顾名思义,使用成员变量引用的方式,一个组件和不同的装饰者自由结合,这就是组合;委托Delegation呢,看这段代码

//获取价格; 0.5是巧克力酱的价格;
    @Override
    public double cost() {
        return iceCream.cost() + 0.5;
    }

用巧克力酱包装冰淇淋,计算价格时,我先调用被装饰者的cost()方法,在返回结果基础上加上巧克力酱的价格;即,执行装饰者的方法时,先委托被装饰者执行,在此基础上加上装饰者的逻辑。是不是有点眼熟?没错,这跟子类先调用父类方法,再扩展子类逻辑很相似,因此呢,我们可以利用组合委托的方式实现继承的功能,区别在于,前者在运行时决定,后者在编译时就决定了。

装饰者模式与继承的比较

我们都知道一个设计原则多用组合,少用继承,可是为什么这样子呢?比如上面我们用的冰淇淋的例子,有2个基本组件:原味冰淇淋和抹茶冰淇淋,调味酱有3种:蓝莓酱、芒果酱、巧克力酱。OK,有同学说,我们完全没必要用什么装饰者模式,用继承就可以了,那我们试一下,为了满足不同人的口味,我们需要定义各种不同的IceCream子类:OriginalWithBlueberry, OriginalWithMango, OriginalWithChocolate, MatchaWithBlueberry, MatchaWithMango, MatchaWithChocolate,用简单的排列组合能得出6种可能,才6个类,不多嘛,可是,完了吗?如果我要原味冰淇淋加2份蓝莓酱呢,OriginalWith2Blueberrys?再加1份巧克力酱呢,OriginalWith2BlueberrysAndChocolate?我的天,这样下去,我算算我得写多少个子类,我发现我无从得知,因为顾客的要求是无法一一列举的,比如一些重口味的食客买了一只冰淇淋,却要我加5份蓝莓酱、2份巧克力酱、3份芒果酱,虽然很奇葩,但我也不能拒绝呀。天呐,这样子我得扩展多少个IceCream子类哦?!!明显这里用装饰者模式要合适得多。

组合关系

  1. 运行时扩展,在运行时可以选择不同的装饰者来进行组合,参考冰淇淋例子
  2. 是has a的关系
  3. 不破坏封装,整体类和局部类松耦合,相对独立
  4. 整体类对局部类进行包装,在被装饰者的行为之前/之后加上自己的行为,以达到特定的目的
  5. 利用组合composition和委托delegation可以在运行时具有继承的效果,并且更有弹性,更好维护

继承关系

  1. 编译时静态扩展,运行时无法动态扩展,因为子类无法改变已继承的父类
  2. 是is a的关系
  3. 破坏封装,子类和父类紧密耦合,子类依赖父类,缺乏独立性
  4. 子类可能从父类继承了并不需要的功能,造成子类冗余
  5. 要实现多重继承的设计时,java的extends无法实现,因为java只支持单继承,这时需要使用组合使得一个类包含多个类的功能

装饰者模式在java中的应用

装饰者模式在java中的典型应用就是输入输出流了。
这里写图片描述
这里FilterInputStream就是输入流包装类(装饰者),BufferedInputStream为输入流组件增强了缓存输入的功能,LineNumberInputStream为输入流组件增强了追踪行号的功能。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值