资讯专栏INFORMATION COLUMN

Java设计模式之装饰模式详解

sewerganger / 2240人阅读

摘要:装饰者模式是动态地将责任附加到对象上。然后我们在子类计算价格的时候加上父类中计算好的配料的价格。结果可乐加冰可乐加冰加糖在的类库中就有很多实际应用到了装饰模式,比如就可以用来装饰,提供更加强大的功能。

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

假设我们有一个需求,是给一家饮料店做一个计算各种饮料价格的功能。听起来很简单,我们创建一个抽象父类Beverages,description用来描述饮料名字,price方法用来计算饮料的价格。

public abstract class Beverages {
    private String description;

    public String getDescription() {
        return description;
    }

    public abstract double price();
}

每种饮料我们都创建一个子类去继承父类,赋值给description,并且重写price方法来设置自己的价格。看上去很完美,我们运行一下看看结果。

public class Coffee extends Beverages {

    public Coffee(String description){
        this.description = description;
    }

    @Override
    public String getDescription() {
        return description;
    }

    @Override
    public double price() {
        return 3.5;
    }
}

public static void main(String[] args){
    Coffee coffee = new Coffee("咖啡");
    System.out.println(coffee.getDescription());
    System.out.println(coffee.price());
}

结果:
咖啡
3.5

但是问题来了,饮料店里不仅仅只有一种饮料,还有可乐、七喜、奶茶其他各种各样的饮料,难道我们要每个都创建一个子类吗?好吧,就算你觉得几十种饮料还不算多,那如果各种饮料直接再进行搭配呢?咖啡加牛奶、咖啡加巧克力、加糖、不加糖,因为各种配料的不同价格和描述也不同,我的天呀,难道要创建几百个类吗?这个时候我们突然想到了不是可以用继承来解决这个问题吗?那我们就来试一下。

我们改造一下Beverages类,把要加的配料用boolean值声明,如果需要添加配料就调用set方法设置为true,在price方法中计算的时候来判断哪一些配料添加了需要计算价格。

public class Beverages {
    public String description;

    public boolean milk;
    public boolean sugar;

    public double milkPrice = 1.5;
    public double sugarPrice = 0.5;

    public boolean isMilk() {
        return milk;
    }

    public void setMilk(boolean milk) {
        this.milk = milk;
    }

    public boolean isSugar() {
        return sugar;
    }

    public void setSugar(boolean sugar) {
        this.sugar = sugar;
    }

    public String getDescription() {
        return description;
    }

    public double price(){
        double basePrice = 0.0;
        if (isMilk()){
            basePrice += milkPrice;
        }
        if (isSugar()){
            basePrice += sugarPrice;
        }
        return basePrice;
    }
}

然后我们在子类计算价格的时候加上父类中计算好的配料的价格。

public class CoffeeWithMilk extends Beverages {

    public CoffeeWithMilk(String description){
        this.description = description;
    }

    @Override
    public double price() {
        return 3.0 + super.price();
    }
}

我们运行看一下,咖啡加牛奶加糖,结果没有问题。

CoffeeWithMilk coffeeWithMilk = new CoffeeWithMilk("咖啡加牛奶加糖");
coffeeWithMilk.setMilk(true);
coffeeWithMilk.setSugar(true);
System.out.println(coffeeWithMilk.getDescription());
System.out.println(coffeeWithMilk.price());

结果:
咖啡加牛奶加糖
5.0

这样一来我们解决了新建无数个子类的问题,但是我们发现单纯继承的做法还是有太多弊端,比如说如果我们想要新添加配料,我们得修改在父类Beverages中添加新的调料字段,还要修改price方法,这严重违反了开发-关闭的设计原则,类应该对扩展开发,对修改关闭。而且有些饮料和配料是没办法搭配的,例如啤酒加糖,但是子类还是会继承到这些配料,并且如果是要同一份配料要加双份又该怎么改呢?所以单纯继承的方法还是不行。这时候我们就要使用到装饰者模式。

首先我们创建抽象父类Beverages,这个父类只是饮料的父类,不是配料的父类。我们创建一个Cola类直接继承它。

public abstract class Beverages {
    public String description;

    public String getDescription() {
        return description;
    }

    public abstract double price();
}

public class Cola extends Beverages {

    public Cola(String description) {
        this.description = description;
    }

    @Override
    public double price() {
        return 2.0;
    }
}

现在就缺配料的部分的代码了,我们再创建一个配料的抽象类Seasonings,我们让它继承Berverages类,并且声明了一个Berverages的引用和抽象的getDescription方法,这里我们稍后再作解释。

public abstract class Seasonings extends Beverages{
    public Beverages beverages;
    public abstract String  getDescription();
}

接下来我们看看配料的实现类怎么写。我们让他继承了父类Seasonings,构造函数接收父类Beverages类型,其实也就是要被装饰的对象,也就是各种各样需要加配料的饮料。然后我们重写了getDescription,我们在传进来的那个Beverages对象的基础上添加名字,price方法也是同理。这样其实就在给传进来的对象外面做了一层装饰,也就是给饮料添加了配料。

public class Ice extends Seasonings {

    public Ice(Beverages beverages) {
        this.beverages = beverages;
    }

    @Override
    public String getDescription() {
        return beverages.getDescription() + "加冰";
    }

    @Override
    public double price() {
        return beverages.price() + 0.2;
    }
}

运行一下,看看结果。没有问题,可以搭配成功。

Cola cola = new Cola("可乐");

Beverages ice = new Ice(cola);
System.out.println(ice.getDescription());
System.out.println(ice.price());

结果:
可乐加冰
2.2

我们还想再添加别的配料,再创建一个新的配料。

public class Sugar extends Seasonings {

    public Sugar(Beverages beverages) {
        this.beverages = beverages;
    }

    @Override
    public String getDescription() {
        return beverages.getDescription() + "加糖";
    }

    @Override
    public double price() {
        return 0.5 + beverages.price();
    }
}

我们在原来搭配好的基础上再进行装饰,结果也是没有问题的。这个时候就要提到为什么我们的Seasonings类要继承Beverages类了,因为每个配料的实现类里有一个Beverages类型的引用,这样我们才可以对它的子类进行装饰,我们让Seasonings也继承Beverages那它的子类也是Beverages类型,我们就可以想装饰几层就装饰几层。

Beverages ice = new Ice(cola);
System.out.println(ice.getDescription());
System.out.println(ice.price());

Beverages sugar = new Sugar(ice);
System.out.println(sugar.getDescription());
System.out.println(sugar.price());

结果:
可乐加冰
2.2
可乐加冰加糖
2.7

在Java的类库中就有很多实际应用到了装饰模式,比如BufferedInputStream就可以用来装饰FileInputStream,提供更加强大的功能。

总结:
装饰模式就是装饰者和被装饰者都具有相同的超类,装饰者拿到被装饰者的引用之后,在调用被装饰者的方法的同时再加上自己的新功能,从而实现了功能的增加,也不需要修改原来的代码。而且因为是相同的超类,所以可以装饰很多层。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/69053.html

相关文章

  • Java 设计模式装饰模式

    摘要:装饰者模式组成结构抽象构件给出抽象接口或抽象类,以规范准备接收附加功能的对象。装饰者模式图解装饰者模式应用场景需要扩展一个类的功能,或给一个类添加附加职责。装饰者对象接受所有来自客户端的请求。参考资料设计模式 一、了解装饰者模式 1.1 什么是装饰者模式 装饰者模式指的是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰者来包裹真实的对...

    kumfo 评论0 收藏0
  • 设计模式装饰模式

    摘要:于是乎,老李家去车行把车进行了改装,车速上去了,媳妇也领到了,皆大欢喜这其实就是一个装饰器的使用案例,原来的车已经没法满足了,所以进行了速度增强,其他功能并没有改变。 装饰器设计模式的概念 在实际生产中,某个类的行为(它所提供的方法)已经没法满足当前的需要了,但是又需要使用原有的部分功能,因此需要对原有对象进行增强——装饰器设计模式(Decorator Pattern)也叫包装器模式就...

    30e8336b8229 评论0 收藏0
  • Java学习路线总结,搬砖工逆袭Java架构师(全网最强)

    摘要:哪吒社区技能树打卡打卡贴函数式接口简介领域优质创作者哪吒公众号作者架构师奋斗者扫描主页左侧二维码,加入群聊,一起学习一起进步欢迎点赞收藏留言前情提要无意间听到领导们的谈话,现在公司的现状是码农太多,但能独立带队的人太少,简而言之,不缺干 ? 哪吒社区Java技能树打卡 【打卡贴 day2...

    Scorpion 评论0 收藏0
  • 「码个蛋」2017年200篇精选干货集合

    摘要:让你收获满满码个蛋从年月日推送第篇文章一年过去了已累积推文近篇文章,本文为年度精选,共计篇,按照类别整理便于读者主题阅读。本篇文章是今年的最后一篇技术文章,为了让大家在家也能好好学习,特此花了几个小时整理了这些文章。 showImg(https://segmentfault.com/img/remote/1460000013241596); 让你收获满满! 码个蛋从2017年02月20...

    wangtdgoodluck 评论0 收藏0
  • 设计模式装饰模式

    摘要:相关设计模式装饰者模式和代理模式装饰者模式关注再一个对象上动态添加方法代理模式关注再对代理对象的控制访问,可以对客户隐藏被代理类的信息装饰着模式和适配器模式都叫包装模式关于新职责适配器也可以在转换时增加新的职责,但主要目的不在此。 0x01.定义与类型 定义:装饰模式指的是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的...

    chuyao 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<