三分钟带您搞懂装饰模式

全文:959字,预计阅读时间:8分钟

定义:

  装饰模式(Decorator)动态地给一个对象添加一些额外的职责。就增加功能来说,装饰模式比生成子类更加灵活。

  这里可以举一个生活中的例子,一个蛋糕,在蛋糕上摆上水果,这个蛋糕就变成了水果蛋糕,给这个水果蛋糕插上蜡烛,它就变成了一个生日蛋糕。(这是Head First中的一个例子,个人觉得非常的形象,记忆犹新)。

分析:

  如果我们需要扩展一个类的功能,你会怎么做呢?如果直接修改这个类,我们就违反了开闭原则(对修改关闭,对扩展开放)。

  我们可以继承这个类,写一个这个类的子类,用于实现扩展功能,也可以使用组合(将这个类作为成员变量),达到相同的效果。

  我们将继承这种关系称为is-a,组合这种关系称为use-a。is-a的耦合程度要高于use-a,所以我们经常可以听到这样的说法:组合优于继承,原因就是这两种关系的耦合程度不同。

装饰模式的核心思想,其实就是用组合代替继承。

图解:

实例:

  这里举一个咖啡店的例子,咖啡的原料是咖啡豆,我们可以使用咖啡豆和牛奶、蜂蜜、摩卡组合出不同价格、不同口味的咖啡。

  这里咖啡豆就是被装饰的对象,也就是图示中的ConcreteComponent,饮品类就是我们抽象出的Component,定义了展示价格和材料两个方法。牛奶、蜂蜜、摩卡是装饰对象,也就是图中的ConcreteDecoratorA、ConcreteDecoratorB。他们抽象出的Decorator,同样定义了展示价格和材料两个方法,具体类图与实现如下:

代码:

/**
 * 饮品.
 *
 * @author jialin.li
 * @date 2019-12-26 22:58
 */
public interface Beverage {
    /** 获取描述 */
    String getDescription();
    /** 获取金额 */
    double getPrice();
}
/**
 * 咖啡豆1
 *
 * @author jialin.li
 * @date 2019-12-26 22:59
 */
public class CoffeeBean1 implements Beverage {

    @Override
    public String getDescription() {
        return "第一种咖啡豆";
    }

    @Override
    public double getPrice() {
        return 10d;
    }
}
/**
 * 咖啡豆2
 *
 * @author jialin.li
 * @date 2019-12-26 23:00
 */
public class CoffeeBean2 implements Beverage{
    @Override
    public String getDescription() {
        return "第一种咖啡豆";
    }

    @Override
    public double getPrice() {
        return 12.5d;
    }
}
/**
 * 装饰器.
 *
 * @author jialin.li
 * @date 2019-12-26 23:02
 */
public class Decorator implements Beverage{

    protected Beverage coffee;

    @Override
    public String getDescription() {
        return "装饰器,由子类重写方法";
    }

    @Override
    public double getPrice() {
        return 0;
    }
}
/**
 * 蜂蜜.
 *
 * @author jialin.li
 * @date 2019-12-26 23:07
 */
public class Honey extends Decorator {
    public Honey(Beverage coffee) {
        this.coffee = coffee;
    }

    @Override
    public String getDescription() {
        return coffee.getDescription() + "加蜂蜜";
    }

    @Override
    public double getPrice() {
        return coffee.getPrice() + 4.5d;
    }
}
/**
 * 牛奶.
 *
 * @author jialin.li
 * @date 2019-12-26 23:03
 */
public class Milk extends Decorator {
    public Milk(Beverage coffee) {
        this.coffee = coffee;
    }

    @Override
    public String getDescription() {
        return coffee.getDescription() + "加牛奶";
    }

    @Override
    public double getPrice() {
        return coffee.getPrice() + 1.5d;
    }
}
/**
 * 摩卡.
 *
 * @author jialin.li
 * @date 2019-12-26 23:05
 */
public class Mocha extends Decorator {
    public Mocha(Beverage coffee) {
        this.coffee = coffee;
    }

    @Override
    public String getDescription() {
        return coffee.getDescription() + "加摩卡";
    }

    @Override
    public double getPrice() {
        return coffee.getPrice() + 2.5d;
    }
}
/**
 * 测试类.
 *
 * @author jialin.li
 * @date 2019-12-26 23:09
 */
public class Main {
    public static void main(String[] args) {
        CoffeeBean1 coffee1 = new CoffeeBean1();
        CoffeeBean2 coffee2 = new CoffeeBean2();

        // 加蜂蜜
        Beverage honey = new Honey(coffee1);
        // 加摩卡
        Beverage mocha = new Mocha(honey);
        System.out.println(mocha.getDescription());
        System.out.println(mocha.getPrice());

        // 加牛奶
        Milk milk = new Milk(coffee2);
        System.out.println(milk.getDescription());
        System.out.println(milk.getPrice());
    }
}

结果:

  第一种咖啡豆加蜂蜜加摩卡

  17.0

  第一种咖啡豆加牛奶

  14.0

原文地址:https://www.cnblogs.com/nedulee/p/12105111.html

时间: 2024-08-30 03:07:17

三分钟带您搞懂装饰模式的相关文章

3分钟带你搞懂ES6 import 和 export

如下语句是 default import: // B.js import A from './A' 且只在A存在 default export 时生效: // A.js export default 42 这种情况下你用import语句, 随便取什么名字都没关系: // B.js import A from './A' import MyA from './A' import Something from './A' 因为他最终解析的是A.js 的 default export. 如下是命名为A

少啰嗦!一分钟带你读懂Java的NIO和经典IO的区别

1.引言 很多初涉网络编程的程序员,在研究Java NIO(即异步IO)和经典IO(也就是常说的阻塞式IO)的API时,很快就会发现一个问题:我什么时候应该使用经典IO,什么时候应该使用NIO? 在本文中,将尝试用简明扼要的文字,阐明Java NIO和经典IO之间的差异.典型用例,以及这些差异如何影响我们的网络编程或数据传输代码的设计和实现的. 本文没有复杂理论,也没有像网上基它文章一样千篇一律的复制粘贴,有的只是接地气的通俗易懂,希望能给你带来帮助. (本文同步发布于:http://www.5

【 全干货 】5 分钟带你看懂 Docker !

欢迎大家前往腾讯云社区,获取更多腾讯海量技术实践干货哦~ 作者丨唐文广:腾讯工程师,负责无线研发部地图测试. 导语:Docker,近两年才流行起来的超轻量级虚拟机,它可以让你轻松完成持续集成.自动交付.自动部署,并且实现开发环境.测试环境.运维环境三方环境的真正同步.本文从Docker定义,作用,技术架构,安装和使用等全方位带你看懂Docker. Docker是啥? 打开翻译君输入Docker 结果显示码头工人,没错!码头工人搬运的是集装箱,那么今天要讲的Docker其操作的也是集装箱,这个集装

面试官最喜欢问的CAS还不会?怎么和他吹牛?!一文带你搞懂CAS

后端开发中大家肯定遇到过实现一个线程安全的计数器这种需求,根据经验你应该知道我们要在多线程中实现?共享变量?的原子性和可见性问题,于是锁成为一个不可避免的话题,今天我们讨论的是与之对应的无锁 CAS.本文会从怎么来的.是什么.怎么用.原理分析.遇到的问题等不同的角度带你真正搞懂 CAS. 为什么要无锁 我们一想到在多线程下保证安全的方式头一个要拎出来的肯定是锁,不管从硬件.操作系统层面都或多或少在使用锁.锁有什么缺点吗?当然有了,不然 JDK 里为什么出现那么多各式各样的锁,就是因为每一种锁都有

一篇文章带你搞懂spring全家桶套餐

spring全家桶里都有哪些食物 上期我们讲了spring和springmvc两个框架的基础知识和学习路线,而这期内容,我们将围绕着spring全家桶展开来讨论. 大家应该都知道,按照出现的顺序,spring全家桶大概包含了spring.springmvc.springboot以及springcloud,从开胃小菜spring到满汉全席springcloud,spring全家桶可谓Java工程师的必备大餐,那么,我们不妨先来看看,spring全家桶是如何从光杆司令spring发展到如今的庞大家族

一篇文章带你搞懂DEX文件的结构

DEX文件就是Android Dalvik虚拟机运行的程序,关于DEX文件的结构的重要性我就不多说了.下面,开练! 建议:不要只看,跟着我做.看再多遍不如自己亲自实践一遍来的可靠,别问我为什么知道.泪崩ing..... 首先,我们需要自己构造一个dex文件,因为自己构造的比较简单,分析起来比较容易.等你简单的会了,难的自然也就懂了. 0x00■  构造DEX文件 首先,我们编写一个简单的Java程序,如下: public class HelloWorld { int a = 0; static

十分钟带你看懂比特币的运行原理

有一种货币目前价值成百上千美元 ,但不是由金子.铂金或任何贵重金属制造的,这就是比特币,那么比特币的原理是什么呢? 针对不方便打开视频的小伙伴,CDA字幕组也贴心的整理了文字版本,如下: 假设有一种货币目前价值成百上千美元 ,但不是由金子.铂金或任何贵重金属制造的,实际上这不是你能放在手中或存钱罐中的钱币. 这是数字货币,意味着它只以电子的形式存在. 我说的就是比特币,比特币的原理与大多数货币很不一样. 它不依附于国家或政府,因此它没有中央发行机构或监管机构.这意味着没有组织机构决定:什么时候要

5分钟带你读懂事务隔离性与隔离级别

前言 我们在上一章节中介绍过数据库的带你了解数据库中事务的ACID特性的相关用法.本章节主要来介绍下数据库中一个非常重要的知识点事务的隔离级别.如有错误还请大家及时指出~ 问题: 事务的隔离级别有哪些? 如果并发事务没有进行隔离,会出现什么问题? 以下都是采用mysql数据库 在多个事务并发做数据库操作的时候,如果没有有效的避免机制,就会出现种种问题.大体上有以下问题: 一.引发的问题 在并发事务没有进行隔离的情况下,会发生如下问题. 问题一:脏读 脏读指一个事务读取了另外一个事务未提交的数据.

顾嘉:5分钟带你读懂《在线》逻辑

互联网成了基础设施,是创新平台:数据成了生产资料,是自然资源 :计算成了公共服务,是能源动力. ——王坚:<在线> 在我的记忆里,云计算.物联网.大数据是近年来被媒体和产业界炒得最热的几个概念了.然而时至今日,尽管有一大批创业公司以大无畏的勇气踏上了探索前路的征途,我们依然很少在现实工作和生活中真切地感受到这几个新技术带来的变化. 除了少部分出类拔萃的企业,大部分产品依旧停留在发布会的PPT中,还没来得及面世就被资本的力量洗刷殆尽.这背后自然有“物竞天择”的无常,也有市场竞争的残酷,但从主观上