装饰者模式

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

实际上装饰者模式就是将继承组合结合在了一起。

这里的一个设计原则是:针对扩展开放,对修改关闭

继承:通过继承子类可以拥有自己定义的方法的同时,还能使用父类方法。但继承不便于扩展父类方法的功能。
组合:是将一个对象传入另一个对象中,由另一个对象的方法调用该对象的方法,并扩展响应功能。与继承相比,组合关系的优势就在于不会破坏类的封装性,且具有较好的松耦合性,可以使系统更加容易维护。但是它的缺点就在于要创建比继承更多的类。

代码示例:
1、先建一个Beverage抽象类,作为饮料的基类。
2、然后依次有四种类型的具体饮料实现HouseBlend、DarkRost、Espresso、Decaf,但都继承自Beverage抽象类。
3、然后定义一个装饰者基类CondimentDecorator,代表调味品基类。
4、具体实现的各种调味品的装饰者,Milk、Moch、Soy、Whip

// 饮料的抽象基类
abstract class Beverage{
    String description = "Unkonwn Beverage";
    public String getDescription() {
        return description;
    }
    public abstract double cost(); 
}
// 四种具体实现的饮料(省略另外两种)
class HouseBlend extends Beverage{
    public HouseBlend() {
        description = "HouseBlend";
    }
    @Override
    public double cost() {
        return 1.99;
    }
}
class Espresso extends Beverage{
    public Espresso() {
        description = "Espresso";
    }
    @Override
    public double cost() {
        return 2.3;
    }
}

装饰者类

// 调料抽象类  装饰者基类
abstract class CondimentDecorator extends Beverage{
    public abstract String getDescription();//要求所有具体的调料类必须实现这个方法
}
// 具体调料类  
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();
    }
}
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 beverage.cost()+0.30;
    }
}

调用测试

    public static void main(String[] args) {
        Beverage b1 = new HouseBlend();
        Beverage b2 = new Espresso();
        // 针对咖啡添加调料
        b1 = new Mocha(b1);// 加摩卡
        b1 = new Milk(b1);// 再加牛奶
        System.out.println("b1:"+b1.cost()+"$ "+b1.getDescription());
        b2 = new Milk(b2);// 只加牛奶
        System.out.println("b2:"+b2.cost()+"$ "+b2.getDescription());
    }

这就相当与给每种类型的咖啡添加很多调料装饰,这种装饰可以任意添加嵌套。

这里解释一下为什么装饰者基类需要继承自一样的超类Beverage
因为装饰者需要和被装饰者(也就是包装的组件,初始时即传入的具体咖啡)具有一样的接口,这个可以通过继承实现。继承的目的只是达到类型匹配,使之具有一样的接口,并不是获得父类方法行为,具体的行为来自于每次传入的被包装的组件。

这里又有一个问题:为什么装饰者需要和被装饰者具有一样的接口?
使用装饰者第一次,装饰完后如:b1 = new Mocha(b1);// 加摩卡,因为装饰者和被装饰者(即组件)继承自同一父类所以具有一样接口,又可以作为一个新的组件传入下一个装饰者进行包装,如:b1 = new Milk(b1);// 再加牛奶

运行结果(结果中出现多位小数原因:是因为计算机无法精确的使用二进制描述一些小数,double类型运算时,有时就会遇到这些数,导致使用近似描述造成误差)

b1:2.4899999999999998$ HouseBlend,Mocha,Milk
b2:2.5999999999999996$ Espresso,Milk

实际中java IO就是使用装饰者模式组织的,这里也引出了一个装饰者模式的一个缺点:含有太多的小类,可能会给第一次看到的人困扰,但了解整个装饰者模式的组织架构,在看这些类就很清晰了。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值