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

目录
  • 一、装饰模式的定义和特点
  • 二、装饰模式的结构
  • 三、咖啡点单案例演示
  • 四、总结

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

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

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

装饰模式的定义:

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

特点:

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

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

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

缺点

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

二、装饰模式的结构

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

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装饰模式内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 深入理解Java设计模式之装饰模式

    目录 一.前言 二.什么是装饰模式 1.定义: 2.意图 3.别名 4.动机 5.作用 6.问题 三.装饰模式的结构 四.装饰模式的使用场景 五.装饰模式的优缺点 六.装饰模式的实现 七.装饰模式的.NET应用 八.总结 一.前言 装饰模式实际上是一直提倡的组合代替继承的实践方式,个人认为要理解装饰者模式首先需要理解为什么需要组合代替继承,继承又是为什么让人深恶痛绝. 为什么建议使用组合代替继承? 面向对象的特性有继承与封装,但两者却又有一点矛盾,继承意味子类依赖了父类中的实现,一旦父类中改变实

  • java设计模式笔记之装饰模式

    一.装饰模式的定义 装饰模式是一种比较常见的模式,其定义如下:Attach additional responsibilities to an object dynamically keeping the same interface.Decorators provide a flexible alternative to subclassing for extending functionality.(动态地给一个对象添加额外的职责.就增加功能来说,装饰模式相比生成子类更为灵活) 装饰模式的通

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

    一.装饰模式引入例子 一个快餐店计算价格问题举例: 快餐店有炒面.炒饭这些快餐,可以额外附加鸡蛋.火腿.培根这些配菜,加配菜需要额外加钱,并且每个配菜的价钱不一样,计算快餐价格如何实现? 1.1 一般设计 1.2 使用继承方式的一般设计存在的问题 横向扩展性不好:如果要再加一种配料(火腿肠),我们就会发现需要给FriedRice和FriedNoodles分别定义一个子类.如果要新增一个快餐品类(炒河粉)的话,就需要定义更多的子,会出现类爆炸的问题. 继承适合于纵向扩展 二.装饰模式 2.1 装饰

  • java设计模式学习之装饰模式

    装饰模式:动态的给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更加灵活. 优点:装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能. 缺点:多层装饰比较复杂. 实例:给一个人配置穿衣 1:代码结构图 2:创建一个person类(  ConcreteComponent) package DecoratorModel; /** * 2017-10-9 10:39:09 * 装饰器设计模式 * Person 类 Concre

  • Java设计模式之装饰模式原理与用法实例详解

    本文实例讲述了Java设计模式之装饰模式原理与用法.分享给大家供大家参考,具体如下: 装饰模式能在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能.它是通过创建一个包装对象,也就是装饰来包裹真实的对象.JDK中IO的设计就用到了装饰模式,通过过滤流对节点流进行包装来实现功能的扩展. 装饰模式的角色的组成: ① 抽象构件(Component)角色:给出一个抽象接口,以规范准备接收附加工功能的对象.(InputStream.OutputStream) ② 具体构件(Concrete Co

  • Java设计模式之代理模式与装饰模式实例详解

    本文实例讲述了Java设计模式之代理模式与装饰模式.分享给大家供大家参考,具体如下: 之所以把这两种模式放在一起说,是因为我发现这了两种模式几乎一模一样! 从网上也搜了一些资料,发现两者还是有一些区别的.我们在学习的同时也把这种困惑搞清楚. 定义: 代理模式,为其他对象提供一种代理以控制对这个对象的访问. 装饰模式,动态地给一个对象添加一些额外的职责. 代理模式,很好理解,就是把一个对象再次封装,以后就对封装的对象访问就可以了. 因为代理对象已经取代了被代理对象. 装饰模式,给一个对象增加功能,

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

    目录 一.装饰模式的定义和特点 二.装饰模式的结构 三.咖啡点单案例演示 四.总结 一.装饰模式的定义和特点 在软件开发过程中,有时想用一些现存的组件.这些组件可能只是完成了一些核心功能.但在不改变其结构的情况下,可以动态地扩展其功能.所有这些都可以釆用装饰器模式来实现. 就像我们做菜,需要用到调料,菜,刀,火等一系列抽象的组件来最终完成一道菜. 装饰模式的定义: 指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式,它属于对象结构型模式.就增加功能来说,装饰模式

  • 详解java设计模式中的门面模式

    门面模式又叫外观模式(Facade Pattern),主要用于隐藏系统的复杂性,并向客户端提供了一个客户端可以访问系统的接口. 我们知道电视剧操作很简单,但是里面的设计和原理很少人明白,这就是因为电视剧的设计应用了门面模式 一个电视剧至少需要有以下几个模块的功能:信号输入.音频处理.视频处理.信号输出等 /** * 射频信号输入 */ public class SignalIn { // } * 音频/视频信号输出 public class SignalOut { * 音频处理 public c

  • 详解JAVA设计模式之代理模式

    什么是设计模式(Design Pattern)? 设计模式是一套被反复使用,多数人知晓的,经过分类编目的,代码设计经验的总结. 代理模式的定义? 代理模式就是为其他对象提供一种代理,以控制对这个对象的访问. 代理对象起到中介作用,可去掉功能服务或增加额外的服务. 代理对象和目标对象的关系? 代理对象:增强后的对象 目标对象:被增强的对象 他们不是绝对的,会根据情况发生变化. 代理模式的两种实现方式? 1.静态代理:代理和被代理对象在代理之前是确定的,它们都实现相同的接口或者继承相同的抽象类. 2

  • 详解JAVA设计模式之模板模式

    在模板模式(Template Pattern)中,一个抽象类公开定义了执行它的方法的方式/模板.它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行.这种类型的设计模式属于行为型模式. 介绍 意图:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中.模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤. 主要解决:一些方法通用,却在每一个子类都重新写了这一方法. 何时使用:有一些通用的方法. 如何解决:将这些通用算法抽象出来. 关键代码:在抽象类实现,其他步骤在子

  • 详解JAVA设计模式之适配器模式

    适配器模式 适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁.这种类型的设计模式属于结构型模式,它结合了两个独立接口的功能. 这种模式涉及到一个单一的类,该类负责加入独立的或不兼容的接口功能.举个真实的例子,读卡器是作为内存卡和笔记本之间的适配器.您将内存卡插入读卡器,再将读卡器插入笔记本,这样就可以通过笔记本来读取内存卡. 我们通过下面的实例来演示适配器模式的使用.其中,音频播放器设备只能播放 mp3 文件,通过使用一个更高级的音频播放器来播放 vlc 和 mp4

  • 详解JAVA 设计模式之状态模式

    在状态模式(State Pattern)中,类的行为是基于它的状态改变的.这种类型的设计模式属于行为型模式. 在状态模式中,我们创建表示各种状态的对象和一个行为随着状态对象改变而改变的 context 对象. 介绍 意图: 允许对象在内部状态发生改变时改变它的行为,对象看起来好像修改了它的类. 主要解决: 对象的行为依赖于它的状态(属性),并且可以根据它的状态改变而改变它的相关行为. 何时使用: 代码中包含大量与对象状态有关的条件语句. 如何解决: 将各种具体的状态类抽象出来. 关键代码: 通常

  • 详解JAVA Spring 中的事件机制

    说到事件机制,可能脑海中最先浮现的就是日常使用的各种 listener,listener去监听事件源,如果被监听的事件有变化就会通知listener,从而针对变化做相应的动作.这些listener是怎么实现的呢?说listener之前,我们先从设计模式开始讲起. 观察者模式 观察者模式一般包含以下几个对象: Subject:被观察的对象.它提供一系列方法来增加和删除观察者对象,同时它定义了通知方法notify().目标类可以是接口,也可以是抽象类或具体类. ConcreteSubject:具体的

  • 详解java设计模式之六大原则

    一.单一职责原则 1.单一职责定义 单一职责原则:一个类只负责一个功能领域中的相应职责,或者可以定义为:就一个类而言,应该只有一个引起它变化的原因. 单一职责原则告诉我们:一个类不能太"累"!在软件系统中,一个类承担的职责越多,它被复用的可能性就越小,而且一个类承担的职责过多,就相当于将这些职责耦合在一起,当其中一个职责 变化时,可能会影响其他职责的运作,因此要将这些职责进行分离,将不同的职责封装在不同的类中,即将不同的变化原因封装在不同的类中,如果多个职责总是同时发生改变则可将它们封

  • 详解Java设计模式之桥接模式

    目录 一.什么是桥接模式: 二.UML结构图 三.代码实现 四.JDBC源码解析-桥接模式 1.源码分析 2.源码类 3.对 JDBC 的观点 一.什么是桥接模式: 桥接,顾名思义,就是用来连接两个部分,使得两个部分可以互相通讯,桥接模式的作用就是为被分离的抽象部分和实现部分搭桥.在现实生活中一个物品在搭配不同的配件时会产生不同的动作和结果,例如一辆赛车搭配的是硬胎或者是软胎就能够在干燥的马路上行驶,而如果要在下雨的路面行驶,就需要搭配雨胎了,这种根据行驶的路面不同,需要搭配不同的轮胎的变化的情

  • 详解java代码中init method和destroy method的三种使用方式

    在java的实际开发过程中,我们可能常常需要使用到init method和destroy method,比如初始化一个对象(bean)后立即初始化(加载)一些数据,在销毁一个对象之前进行垃圾回收等等. 周末对这两个方法进行了一点学习和整理,倒也不是专门为了这两个方法,而是在巩固spring相关知识的时候提到了,然后感觉自己并不是很熟悉这个,便好好的了解一下. 根据特意的去了解后,发现实际上可以有三种方式来实现init method和destroy method. 要用这两个方法,自然先要知道这两

随机推荐