详解Java设计模式中的装饰模式

 更新时间:2021年12月30日 15:48:38   作者:空山新雨后~  
装饰模式是指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式,它属于对象结构型模式。本文将为大家详细介绍一下装饰模式,感兴趣的可以了解一下

一、装饰模式的定义和特点

在软件开发过程中,有时想用一些现存的组件。这些组件可能只是完成了一些核心功能。但在不改变其结构的情况下,可以动态地扩展其功能。所有这些都可以釆用装饰器模式来实现。

就像我们做菜,需要用到调料,菜,刀,火等一系列抽象的组件来最终完成一道菜。

装饰模式的定义:

指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式,它属于对象结构型模式。就增加功能来说,装饰模式比生成子类更加灵活。

特点:

- 装饰器是继承的有力补充,比继承灵活,在不改变原有对象的情况下,动态的给一个对象扩展功能,即插即用

- 通过使用不用装饰类及这些装饰类的排列组合,可以实现不同效果

- 装饰器模式完全遵守开闭原则

缺点

装饰器模式会增加许多子类,过度使用会增加程序得复杂性。

二、装饰模式的结构

装饰模式的结构一般包含以下几个角色

1. 抽象构件(Component)角色:定义一个抽象接口以规范准备接收附加责任的对象。

2. 具体构件(ConcreteComponent)角色:实现抽象构件,通过装饰角色为其添加一些职责。

3. 抽象装饰(Decorator)角色:继承抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能。

4. 具体装饰(ConcreteDecorator)角色:实现抽象装饰的相关方法,并给具体构件对象添加附加的责任。

图示

三、咖啡点单案例演示

有一个需求,点一杯咖啡需要咖啡,材料等等,这个案例就很适合装饰模式,类似于穿衣,点餐,买包子,等等,我们怎么把他设计成装饰模式呢?

看类图

这个结构就是我已经设计好的一个装饰模式的类图,idea自动生成的,这里的Drink就是我们上面说的抽象构建角色,装饰者是Decorator,他是一个抽象装饰,下面他的子类就是具体的装饰者,那么具体构建中间我们提供了一个中间构建,提供了coffee的一些共性,可以放在这里,用的时候直接继承,他的下面就是相应的具体构件,具体被装饰者角色,装饰者与被装饰者共同继承自component抽象构件,需要用到装饰的就是我们点一杯咖啡,用装饰去包裹即可,层层包裹,案例如下:

比如我要点一份加糖加奶的拿铁咖啡

代码实例:

component抽象构件角色:

package com.decoratorPattern.starBucks;
 
/**
 * @author wang
 * @version 1.0
 * @packageName com.decoratorPattern.starBucks
 * @className Drink
 * @date 2021/12/28 10:28
 * @Description 饮料构件类抽象component
 */
public abstract class Drink {
    private String description;
    private float price = 0.0f;
 
    public String getDescription() {
        return description;
    }
 
    public void setDescription(String description) {
        this.description = description;
    }
 
    public float getPrice() {
        return price;
    }
 
    public void setPrice(float price) {
        this.price = price;
    }
 
    /**
     * @Date  2021/12/28 10:30
     * @Param
     * @Return float
     * @MetodName cost
     * @Author wang
     * @Description 计算花费,订单价格
     */
    public abstract float cost();
}

装饰者类:

package com.decoratorPattern.starBucks;
 
/**
 * @author wang
 * @version 1.0
 * @packageName com.decoratorPattern.starBucks
 * @className Decorator
 * @date 2021/12/28 10:40
 * @Description 装饰者定义类,配料
 */
public class Decorator extends Drink {
 
    private Drink drink;
 
    /**
     * @param drink
     * @Date 2021/12/28 10:42
     * @Param
     * @Return null
     * @MetodName Decorator
     * @Author wang
     * @Description 传入一个被装饰者,由装饰者进行装饰
     */
    public Decorator(Drink drink) {
        this.drink = drink;
    }
 
    /**
     * @Date 2021/12/28 10:43
     * @Param
     * @Return float
     * @MetodName cost
     * @Author wang
     * @Description 装饰者的价格加上被装饰者的价格
     */
    @Override
    public float cost() {
        return super.getPrice() + drink.cost();
    }
 
    /**
     * @Date 2021/12/28 10:44
     * @Param
     * @Return String
     * @MetodName getDescription
     * @Author wang
     * @Description 输出订单信息,包含装饰者,装饰者的价格,以及被装饰者的信息
     */
    @Override
    public String getDescription() {
        return drink.getDescription() + "\n加入的材料:" + super.getDescription()
                + "\t材料价格:" + super.getPrice() ;
    }
}
package com.decoratorPattern.starBucks;
 
/**
 * @author wang
 * @version 1.0
 * @packageName com.decoratorPattern.starBucks
 * @className Coffee
 * @date 2021/12/28 10:31
 * @Description 咖啡类
 */
public class Coffee extends Drink{
    @Override
    public float cost() {
        return super.getPrice();
    }
}

具体构件类:拿铁

package com.decoratorPattern.starBucks;
 
/**
 * @author wang
 * @version 1.0
 * @packageName com.decoratorPattern.starBucks
 * @className latte
 * @date 2021/12/28 10:32
 * @Description 拿铁咖啡实类,被装饰者
 */
public class Latte extends Coffee{
    public Latte() {
        setDescription("拿铁咖啡");
        setPrice(15.0f);
    }
}

具体构件类:摩卡

package com.decoratorPattern.starBucks;
 
/**
 * @author wang
 * @version 1.0
 * @packageName com.decoratorPattern.starBucks
 * @className Mocha
 * @date 2021/12/28 10:36
 * @Description 摩卡咖啡实类,被装饰者
 */
public class Mocha extends Coffee {
    public Mocha() {
        setDescription("摩卡咖啡");
        setPrice(12.2f);
    }
}

其他同上,不过多展示

具体装饰类:牛奶

package com.decoratorPattern.starBucks;
 
/**
 * @author wang
 * @version 1.0
 * @packageName com.decoratorPattern.starBucks
 * @className Milk
 * @date 2021/12/28 10:47
 * @Description 牛奶调味品,具体装饰者
 */
public class Milk extends Decorator{
 
    /**
     * @param drink
     * @Date 2021/12/28 10:42
     * @Param
     * @Return null
     * @MetodName Decorator
     * @Author wang
     * @Description 传入一个被装饰者,由装饰者进行装饰
     */
    public Milk(Drink drink) {
        super(drink);
        setDescription("牛奶");
        setPrice(1.0f);
    }
}

具体装饰:糖

package com.decoratorPattern.starBucks;
 
/**
 * @author wang
 * @version 1.0
 * @packageName com.decoratorPattern.starBucks
 * @className sugar
 * @date 2021/12/28 10:50
 * @Description 糖,装饰者
 */
public class Sugar extends Decorator{
    /**
     * @param drink
     * @Date 2021/12/28 10:42
     * @Param
     * @Return null
     * @MetodName Decorator
     * @Author wang
     * @Description 传入一个被装饰者,由装饰者进行装饰
     */
    public Sugar(Drink drink) {
        super(drink);
        setDescription("糖");
        setPrice(0.5f);
    }
}

订单测试代码:

package com.decoratorPattern.starBucks;
 
/**
 * @author wang
 * @version 1.0
 * @packageName com.decoratorPattern.starBucks
 * @className OrderTest
 * @date 2021/12/28 10:51
 * @Description 前台订单类
 */
public class OrderTest {
    public static void main(String[] args) {
        //点一份加糖加奶的拿铁咖啡
 
        System.out.println("+++++++没加任何东西+++++++");
        Drink latte = new Latte();
        System.out.println("当前总价:" + latte.cost());
        System.out.println("coffee:" +latte.getDescription());
        //加糖
        System.out.println("+++++++加糖后+++++++");
        latte = new Sugar(latte);
        System.out.println("当前总价:" + latte.cost());
        System.out.println("coffee:" + latte.getDescription());
 
        System.out.println("+++++++加奶后+++++++");
        latte = new Milk(latte);
        System.out.println("当前总价:" + latte.cost());
        System.out.println("coffee:" +latte.getDescription());
    }
}
/**
 * +++++++没加任何东西+++++++
 * 当前总价:15.0
 * coffee:拿铁咖啡
 * +++++++加糖后+++++++
 * 当前总价:15.5
 * coffee:拿铁咖啡
 * 加入的材料:糖	材料价格:0.5
 * +++++++加奶后+++++++
 * 当前总价:16.5
 * coffee:拿铁咖啡
 * 加入的材料:糖	材料价格:0.5
 * 加入的材料:牛奶	材料价格:1.0
 *
 * Process finished with exit code 0
 */

综上, 如果我们需要新的咖啡种类或者是新的调料,只需要新增类去继承coffee或者decorator类即可。

四、总结

装饰模式是为已有的功能动态的添加更多功能的一种方式,当系统需要新功能的时候,向旧的类中添加新的代码,这些新加的代码通常装饰了原有类的核心职责或主要行为。

优点:

把类中装饰功能从类中移除,这样可以简化原来的类,

有效的把类的核心职责和装饰功能分开了,而且可以去除相关类中的重复装饰逻辑

可代替继承。 

到此这篇关于详解Java设计模式中的装饰模式的文章就介绍到这了,更多相关Java装饰模式内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • SpringBoot在项目中访问静态资源步骤分析

    SpringBoot在项目中访问静态资源步骤分析

    今天在玩SpringBoot的demo的时候,放了张图片在resources目录下,启动区访问的时候,突然好奇是识别哪些文件夹来展示静态资源的, 为什么有时候放的文件夹不能显示,有的却可以
    2023-01-01
  • 详细学习Java Cookie技术(用户登录、浏览、访问权限)

    详细学习Java Cookie技术(用户登录、浏览、访问权限)

    这篇文章主要为大家详细介绍了Java Cookie技术,显示用户上次登录的时间、显示用户最近浏览的若干个图片(按比例缩放)等,感兴趣的小伙伴们可以参考一下
    2016-08-08
  • Spring Boot用户注册验证的实现全过程记录

    Spring Boot用户注册验证的实现全过程记录

    最近在设计自己的博客系统,涉及到用户注册与登录验证,所以下面这篇文章主要给大家介绍了关于Spring Boot用户注册验证的实现全过程,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-01-01
  • 几种SpringBoot的属性配置方式详解

    几种SpringBoot的属性配置方式详解

    通常项目配置信息都写在.properties或者.yml文件中,但是打成jar包部署后,如果需要修改配置信息,还需要改完再重新打包部署,因此,下面介绍几种SpringBoot的属性配置方式,需要的朋友可以参考下
    2024-12-12
  • Java Comparator.comparing比较导致空指针异常的解决

    Java Comparator.comparing比较导致空指针异常的解决

    这篇文章主要介绍了Java Comparator.comparing比较导致空指针异常的解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-07-07
  • Java中Arrays.asList()方法详解及实例

    Java中Arrays.asList()方法详解及实例

    这篇文章主要介绍了Java中Arrays.asList()方法将数组作为列表时的一些差异的相关资料,需要的朋友可以参考下
    2017-06-06
  • SpringBoot中的ApplicationListener事件监听器使用详解

    SpringBoot中的ApplicationListener事件监听器使用详解

    这篇文章主要介绍了SpringBoot中的ApplicationListener事件监听器使用详解,ApplicationListener是应用程序的事件监听器,继承自java.util.EventListener标准接口,采用观察者设计模式,需要的朋友可以参考下
    2023-11-11
  • 利用Java计算某个日期是星期几

    利用Java计算某个日期是星期几

    不知道大家有没有遇到过同样的问题,谁谁的生日又要到了,看看是星期几?每年都要遇到好几次,所以想索性利用Java写个小工具,一次查询某具体日期在n年中分别是星期几。这样不就方便了吗?本文里给出了详细的示例代码,感兴趣的朋友们下面来一起看看吧。
    2016-10-10
  • SpringBoot实用小技巧之如何动态设置日志级别

    SpringBoot实用小技巧之如何动态设置日志级别

    这篇文章主要给大家介绍了关于SpringBoot实用小技巧之如何动态设置日志级别的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用SpringBoot具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-04-04
  • Springboot Thymeleaf数字对象使用方法

    Springboot Thymeleaf数字对象使用方法

    这篇文章主要介绍了Springboot Thymeleaf数字对象使用方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2007-09-09

最新评论