23种设计模式——装饰者模式-程序员宅基地

技术标签: Decorator  装饰者模式  设计模式  23种设计模式  

23种设计模式——装饰者模式

1、装饰者模式概述

背景

有些人为了早上多睡一会,就会用方便的方式解决早餐问题。有些人早餐可能会吃煎饼,煎饼中可以加鸡蛋,也可以加香肠,但是不管怎么“加码”,都还是一个煎饼。在现实生活中,常常需要对现有产品增加新的功能或美化其外观,如房子装修、相片加相框、咖啡加调料等,都是装饰器模式。

装饰者模式的定义

装饰者(Decorator)模式的定义:指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式。

装饰者模式属于对象结构型模式,也体现了开闭原则(ocp)。

装饰者模式的优缺点

优点

  • 装饰器是继承的有力补充,比继承灵活,在不改变原有对象的情况下,动态的给一个对象扩展功能,即插即用
  • 通过使用不用装饰类及这些装饰类的排列组合,可以实现不同效果
  • 装饰器模式完全遵守开闭原则

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

2、装饰者模式的结构

装饰者模式的结构

装饰器模式主要包含以下角色。

  1. 抽象构件(Component)角色:定义一个抽象接口以规范准备接收附加责任的对象。
  2. 具体构件(ConcreteComponent)角色:实现抽象构件,通过装饰角色为其添加一些职责。
  3. 抽象装饰(Decorator)角色:继承抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能。
  4. 具体装饰(ConcreteDecorator)角色:实现抽象装饰的相关方法,并给具体构件对象添加附加的责任。

装饰者模式的结构图

在这里插入图片描述

3、装饰者模式的实现

实现星巴克咖啡+调味品的下单,并计算费用

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

public abstract class Drink {
    

    public String dsc; //描述
    private float price = 0.0f; //价格

    public String getDsc() {
    
        return dsc;
    }

    public void setDsc(String dsc) {
    
        this.dsc = dsc;
    }

    public float getPrice() {
    
        return price;
    }

    public void setPrice(float price) {
    
        this.price = price;
    }

    //计算费用的抽象方法,由子类实现
    public abstract float cost();

}

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

public class Coffee extends Drink{
    
    @Override
    public float cost() {
    
        return super.getPrice();
    }
}

具体构件(ConcreteComponent)角色1:Espresso 意大利咖啡

public class Espresso extends Coffee{
    

    public Espresso(){
    
        setDsc("意大利咖啡");
        setPrice(10.0f);
    }
}

具体构件(ConcreteComponent)角色2:LongBlack 美式咖啡1

public class LongBlack extends Coffee{
    
    
    public LongBlack(){
    
        setDsc("美式咖啡1");
        setPrice(9.0f);
    }
}

具体构件(ConcreteComponent)角色3:LongBlack 美式咖啡2

public class ShortBlack extends Coffee{
    
    
    public ShortBlack(){
    
        setDsc("美式咖啡2");
        setPrice(8.0f);
    }
}

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

public class Decorator extends Drink{
    

    //装饰者组合了抽象构件(被装饰者)
    private Drink obj;

    public Decorator(Drink obj){
    
        this.obj = obj;
    }

    @Override
    public float cost() {
    
        //计算费用:父类的费用 + 自己的费用
        return getPrice() + obj.getPrice();
    }

    @Override
    public String getDsc() {
    
        //输出描述:父类的描述 + 自己的描述
        return dsc + " " + getPrice() + " " +obj.getDsc();
    }
}

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

具体装饰(ConcreteDecorator)角色1:巧克力 Chocolate

public class Chocolate extends Decorator{
    

    public Chocolate(Drink obj) {
    
        super(obj);
        setDsc("巧克力");
        setPrice(3.0f);
    }
}

具体装饰(ConcreteDecorator)角色2:牛奶 Milk

public class Milk extends Decorator{
    
    
    public Milk(Drink obj) {
    
        super(obj);
        setDsc("牛奶");
        setPrice(2.0f);
    }
}

具体装饰(ConcreteDecorator)角色3:豆浆 Soy

public class Soy extends Decorator{
    
    
    public Soy(Drink obj) {
    
        super(obj);
        setDsc("豆浆");
        setPrice(1.5f);
    }
}

编写测试类下咖啡订单,测试装饰者模式:

public class coffeeBar {
    
    public static void main(String[] args) {
    

        //一杯美式
        Drink order1 = new LongBlack();
        System.out.println("订单描述:" + order1.getDsc());
        System.out.println("订单费用= " + order1.cost());

        //一杯美式 + 一份牛奶
        order1 = new Milk(order1);
        System.out.println("订单描述:" + order1.getDsc());
        System.out.println("订单费用= " + order1.cost());

        //一杯美式 + 2份牛奶
        order1 = new Milk(order1);
        System.out.println("订单描述:" + order1.getDsc());
        System.out.println("订单费用= " + order1.cost());

        //一杯美式 + 2份牛奶 + 巧克力
        order1 = new Chocolate(order1);
        System.out.println("订单描述:" + order1.getDsc());
        System.out.println("订单费用= " + order1.cost());
    }
}

在这里插入图片描述

4、装饰者模式的应用场景

装饰者模式的应用场景

  • 当需要给一个现有类添加附加职责,而又不能采用生成子类的方法进行扩充时。例如,该类被隐藏或者该类是终极类或者采用继承方式会产生大量的子类。
  • 当需要通过对现有的一组基本功能进行排列组合而产生非常多的功能时,采用继承关系很难实现,而采用装饰器模式却很好实现。
  • 当对象的功能要求可以动态地添加,也可以再动态地撤销时。

装饰器模式在 Java 中的体现

装饰器模式在 Java语言中的最著名的应用莫过于 Java I/O 标准库的设计了。例如,InputStream 的子类 FilterInputStream,OutputStream 的子类 FilterOutputStream,Reader 的子类 BufferedReader 以及 FilterReader,还有 Writer 的子类 BufferedWriter、FilterWriter 以及 PrintWriter 等,它们都是抽象装饰类。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/wpc2018/article/details/121373758

智能推荐

基于单片机的加热炉炉温控制系统设计-程序员宅基地

文章浏览阅读3.5k次,点赞7次,收藏41次。但由于输出控制量只有两种状态,使被控参数在两个方向上变化的速率均为最大,因此容易引起反馈回路产生振荡,对自动控制加热炉炉温控制系统会产生十分不利的影响,甚至会因为输出开关的频繁动作而不能满足加热炉炉温控制系统对控制精度的要求。但随着计算机与超大规模集成电路的迅速发展,以现代控制理论和计算机为基础,采用数字控制、显示、A/D与D/A转换,配合执行器与控制阀构成的计算机控制加热炉炉温控制系统,在过程控制过程中得到越来越广泛的应用。由于炉温控制加热炉炉温控制系统的控制对象具有惯性大,连续性的特点。_基于单片机的加热炉炉温控制系统设计

Oracle-----约束简介&非空约束&唯一约束&主键约束-程序员宅基地

文章浏览阅读2.1k次,点赞2次,收藏4次。上一篇????:Oracle-----为表重命名&数据表删除&闪回技术&修改表结构文章目录1、约束简介2、非空约束(not null、nk)2.1 范例1:使用非空约束2.2 范例2:正确地增加语句2.3 范例3:错误地增加语句3、唯一约束(unique、uk)3.1 范例1:使用唯一约束3.2 范例2:正确地增加语句3.3 范例3:错误地增加语句3.4 范例4:查询user...

CamVid数据集(智能驾驶场景的语义分割)_camvid数据集11类别-程序员宅基地

文章浏览阅读3.3k次,点赞2次,收藏7次。前言CamVid 数据集是由剑桥大学公开发布的城市道路场景的数据集。CamVid全称:The Cambridge-driving Labeled Video Database,它是第一个具有目标类别语义标签的视频集合。数据集包 括 700 多张精准标注的图片用于强监督学习,可分为训练集、验证集、测试集。同时, 在 CamVid 数据集中通常使用 11 种常用的类别来进行分割精度的评估,分别为:道路 (Road)、交通标志(Symbol)、汽车(Car)、天空(Sky)、行人道(Sidewalk)、电_camvid数据集11类别

ESP32 LVGL8.1 实现太空人显示(29)_lv_img_declare-程序员宅基地

文章浏览阅读8.7k次,点赞11次,收藏90次。文章目录一、ESP32 LVGL工程配置1.1从库中下载LVGL代码1.2配置适合ESP32 液晶屏1.3编译下载测试二、GIF图片处理2.1下载gif图片2.2将gif图片按照帧率导出成图片2.lvgl animimg对象实现图片的播放1.3下载测试 注:本博客作为学习笔记,有错误的地方希望指正一、ESP32 LVGL工程配置首先要通过液晶屏显示太空人,我们这里主要有两种方式可以实现,第一种直接使用厂家只带的液晶屏幕驱动去实现图片的显示,另外使用其他的GUI提供的控件去实现,嵌入式常见的GUI挺多_lv_img_declare

最小权顶点覆盖问题-程序员宅基地

文章浏览阅读1.5w次,点赞7次,收藏30次。问题描述:给定一个赋权无向图G=(V,E),每个顶点v∈V都有一个权值w(v)。如果UV,且对任意(u,v)∈E有u∈U或v∈U,就称U为图G的一个顶点覆盖。G的最小权顶点覆盖是指G中所含顶点权之和最小的顶点覆盖。问题解决:用优先队列分支限界方法解最小权顶点覆盖,在算法的搜索的进程中保存当前已构造出的部分解空间树,在算法搜索达到叶节点时,其最优值对应的最优解同时保存下来。优先队列的优先_最小权顶点覆盖

matlab如何导入map,matlab添加M_map工具箱-程序员宅基地

文章浏览阅读517次。首先试了matlab自带的worldmap,感觉画出来的图形不尽如人意,比较杂乱。如下图。查阅了些资料,请教了Liangjing,一致推荐m_map。为了达到想要的效果,这次只要不再偷懒,下载M-Map工具箱(http://www.eos.ubc.ca/~rich/map.html)并进行安装。所幸过程比较顺利,现记录如下,回头把画出的效果图再添上。其他matlab的toolbox安装,也可参考进..._mmap工具包如何安装

随便推点

android 添加依赖出现Failed to resolve:"你添加的依赖名" 或者出现 debug@Compileclasspath问题_android failed to resolve: com.github.xiaohaibin:x-程序员宅基地

文章浏览阅读4.8w次,点赞11次,收藏16次。首先出现的问题是debug@Compileclasspath的问题,经过一波百度之后都说是AS build.gradle版本3.0以上的问题,但是经过一波修改之后并没有什么卵用,并没有解决这个问题,并且出现了新的问题,就是Failed to resolve:.......这个问题了,这面贴下终极解决方案,希望可以帮到你。问题截图:解决方案:在整个工程的build.gradle中添加以..._android failed to resolve: com.github.xiaohaibin:xbanner:1.8.9

php路由类默认模块,Laravel学习教程之路由模块-程序员宅基地

文章浏览阅读169次。前言本文主要给大家介绍的是关于Laravel路由模块的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧。备注:本文是基于Laravel 5.4版本的路由模块代码进行分析书写;模块组成下图展示了路由模块中各个文件的关系,并进行简要说明;剖析服务提供者看Laravel模块,首先找ServiceProvider文件,这是模块与IOC容器交互的入口,从这个文件,可以看出该模块提供向..._symfony app('router')->getcurrentrequest()

Google Chrome 浏览器 开发者工具 使用教程-程序员宅基地

文章浏览阅读354次。Google Chrome 浏览器 开发者工具 使用教程 12,912 9 3.83 / 5 53.83分(6票)8对于Chrome 浏览器,除了占用内存的缺点,其他都很不错。对于Chrome 浏览器的开发者工具,Jeff 除了Elements、Resources、Network..._开发者使用教程

maven 依赖文件 pom.xml 编译 mvn compile 运行 不用mvn exec:java -Dexec.mainClass="hello.HelloWorld"...-程序员宅基地

文章浏览阅读289次。使用maven编译Java项目http://blog.csdn.net/yaya1943/article/details/48464371使用"mvn clean"命令清除编译结果,也就是把编译生成的target文件夹删掉如果你想安装您的项目的JAR文件到本地Maven仓库,那么你应该调用下面语句:mvn install此时,你的项目代码将会经过编译、测试、打..._maven 编译不使用本地依赖包

巧用ChatGPT快速提高职场晋升力、搞定数据分析玩、转新媒体运营-程序员宅基地

文章浏览阅读7.6k次,点赞147次,收藏134次。在日常工作中巧用ChatGPT可以帮助我们提高工作效率、创造价值并降低成本。通过合理地利用ChatGPT的功能和应用场景,企业和个人可以更好地实现工作目标、提升竞争力并取得更大的成功。随着人工智能技术的不断进步和发展我们相信巧用ChatGPT将成为未来工作中的一种常态化工具为我们的职业生涯和生活带来更多便利和价值。本书是一本关于数据分析与ChatGPT应用的实用指南,旨在帮助读者了解数据分析的基础知识及利用ChatGPT进行高效的数据处理和分析。_巧用chatgpt快速提高职场晋升力

vscode同步git代码时源代码管理出现10k+更改如何处理?_git init 代码有 10k+ 更新-程序员宅基地

文章浏览阅读2.7k次。vscode出现10k+更改需要处理_git init 代码有 10k+ 更新

推荐文章

热门文章

相关标签