java设计模式-装饰者模式详解

目录
  • 引例
  • 一般解法
  • 装饰者模式
  • 装饰者解法
    • 代码:
      • 抽象类
      • 装饰者
      • 被装饰者
      • 客户端测试
  • 总结:

引例

需求:设现在有单品咖啡:Espresso(意大利浓咖啡)和LongBlack(美式咖啡),调料有Milk(牛奶)和sugar(糖),客户可以点单品咖啡或单品咖啡+调料的组合,计算相应费用。要求在扩展新的咖啡种类时,具有良好的扩展性、改动维护方便。

抛砖引玉,我们先看看两种一般解法。

一般解法

方案一、

枚举创建每一种组合可能,Drink抽象类表示饮料,cost()方法计算价格,子类如Longblack_Milk表示美式咖啡加牛奶:

这样设计十分不明智,会有很多类,当新增一个单品咖啡或调料时,类的数量就会倍增,出现类爆炸。

方案二、

把调料内置到Drink类,减少类数量过多:

方案二虽然不至于造成很多类,但是增加或删除调料时,代码维护量仍旧很大。

装饰者模式

装饰者模式(Decorator Pattern)是结构型模式,也称装饰器模式/修饰模式。它可以动态的将新功能附加到对象上,同时又不改变其结构。在对象功能扩展方面,它比继承更有弹性。这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。

类图:

  1. Component抽象类:主体,比如类似前面的Drink。
  2. ConcreteComponent类:具体的主体,比如前面的单品咖啡。
  3. Decorator类:装饰者,比如前面的调料
  4. ConcreteDecorator类:具体的装饰者,比如前面的牛奶。

如果ConcreteComponent具体子类很多,那么可以再加一个中间层,提取共同部分,通过继承实现更多不同的具体子类。

装饰者解法

类图:

Drink 类就是前面说的抽象类Decorator 是一个装饰类,含有一个被装饰的对象(Drink obj)和的cost()方法进行一个费用的叠加计算,递归的计算价格Milk和Suger是具体的装饰者Coffee是被装饰者主体LongBlack和Espresso是具体实现的被装饰者实体

代码:

抽象类

public abstract class Drink {//抽象类
    public String des; // 描述
    private float price = 0.0f;
    public String getDes() {
        return des;
    }
    public void setDes(String des) {
        this.des = des;
    }
    public float getPrice() {
        return price;
    }
    public void setPrice(float price) {
        this.price = price;
    }
    //计算费用的抽象方法
    public abstract float cost();
}

装饰者

public class Decorator extends Drink {//装饰者
    private Drink obj;
    public Decorator(Drink obj) { //组合
        this.obj = obj;
    }
    @Override
    public float cost() {
        // getPrice 自己价格
        return super.getPrice() + obj.cost();
    }
    @Override
    public String getDes() {
        // obj.getDes() 输出被装饰者的信息
        return des + " " + getPrice() + " && " + obj.getDes();
    }
}
public class Milk extends Decorator {//装饰者子类
    public Milk(Drink obj) {
        super(obj);
        setDes(" 牛奶 ");
        setPrice(2.0f);
    }
}
public class Suger extends Decorator {//装饰者子类
    public Suger(Drink obj) {
        super(obj);
        setDes(" 糖 ");
        setPrice(1.0f);
    }
}

被装饰者

public class Coffee  extends Drink {//被装饰者
    @Override
    public float cost() {
        return super.getPrice();
    }
}
public class Espresso extends Coffee {//被装饰者子类
    public Espresso() {
        setDes(" 意式咖啡 ");
        setPrice(6.0f);
    }
}
public class LongBlack extends Coffee {//被装饰者子类
    public LongBlack() {
        setDes(" 美式咖啡 ");
        setPrice(5.0f);
    }
}

客户端测试

public class Client {
    public static void main(String[] args) {
        // 阿姨的卡布奇诺:意式加两份牛奶、一份糖
        // 1. 点一份Espresso
        Drink order = new Espresso();
        System.out.println("order1 费用=" + order.cost());
        System.out.println("order1 描述=" + order.getDes());
        // 2.1 order 加一份牛奶
        order = new Milk(order);
        System.out.println("order 加入一份牛奶 费用 =" + order.cost());
        System.out.println("order 加入一份牛奶 描述 = " + order.getDes());
        // 2.2 order 再加一份牛奶
        order = new Milk(order);
        System.out.println("order 加入两份牛奶 费用 =" + order.cost());
        System.out.println("order 加入两份牛奶 描述 = " + order.getDes());
        // 3. order 加一份糖
        order = new Suger(order);
        System.out.println("order 两份牛奶、一份糖 费用 =" + order.cost());
        System.out.println("order 两份牛奶、一份糖 描述 = " + order.getDes());
        System.out.println("===========================");
        //美式咖啡加一份牛奶
        //1. 点一份LongBlack
        Drink order2 = new LongBlack();
        System.out.println("order2 费用 =" + order2.cost());
        System.out.println("order2 描述 = " + order2.getDes());
        //2. order2 加一份牛奶
        order2 = new Milk(order2);
        System.out.println("order2 加入一份牛奶 费用 =" + order2.cost());
        System.out.println("order2 加入一份牛奶 描述 = " + order2.getDes());
    }
}

运行结果:

总结:

装饰者模式就像打包一个快递,不断的动态添加新的功能,可以组合出所有情况:

第一份Milk包含一份Espresso

第二份Milk包含(Milk+Espresso)

Suger包含(Milk+Milk+Espresso)

本篇文章就到这里了,希望能给你带来帮助,也希望您能够多多关注我们的更多内容!

时间: 2021-07-17

java设计模式--七大原则详解

目录 设计模式 单一职责原则 接口隔离原则 依赖倒转原则 里氏替换原则 开闭原则 迪米特法则 合成复用原则 总结 设计模式 软件设计模式(Design pattern),又称设计模式,是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了可重用代码.让代码更容易被他人理解.保证代码可靠性.程序的重用性. 打个比方就像盖大厦和小木屋,当功能简单,函数和代码少时,我们能较轻松的直接上手:但如果是像大厦那样,功能复杂,需求可能变化且代码量大时,我们就不能直接上手就来,需

java设计模式-组合模式详解

目录 组合模式 Demo 代码: 总结 组合模式 组合模式(Composite Pattern)又叫部分整体模式,是用于把一组相似的对象当作一个单一的对象.组合模式依据树形结构来组合对象,用来表示部分以及整体层次.这种类型的设计模式属于结构型模式,它创建了对象组的树形结构. 主要解决:它在我们树型结构的问题中,模糊了简单元素和复杂元素的概念,客户程序可以像处理简单元素一样来处理复杂元素,从而使得客户程序与复杂元素的内部结构解耦. 如何解决:树枝和叶子实现统一接口,树枝内部组合该接口. 何时使用:

java设计模式--建造者模式详解

目录 引例 一般解法 代码: AbstractHouse(房子) Bungalow(平房) Villa (别墅) 客户端调用 建造者模式 建造者模式解法 代码: House类(Product产品) HouseBuilder类(Builder抽象建造者) 3.Bungalow类(ConcreteBuilder具体建造者A) 4.Villa类(ConcreteBuilder具体建造者B) 5.HouseDirector类(Director指挥者) 客户端调用 总结 引例 需求:建造房子,建造过程包括

java设计模式--三种工厂模式详解

目录 简单工厂 代码: 1.产品接口 2.产品接口实现子类 3.简单工厂类 4.调用工厂 5.测试 工厂方法 代码: 1.工厂接口 2.工厂实现子类 3.产品接口 4.产品实现子类 5.调用 6.测试 1.产品接口 2.产品抽象子类-普通产品 抽象工厂 3.1产品抽象子类-魔法产品 4.工厂接口 5.工厂实现子类-普通工厂 6.工厂实现子类-魔法工厂 7.调用 8.测试 总结 简单工厂 简单工厂模式是属于创建型模式,是工厂模式的一种.简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例.定义

java设计模式--原型模式详解

目录 引例 原型模式 浅拷贝 在原先Sheep类基础上实现Cloneable接口,重写clone方法. 客户端调用 Sheep类 新添的Cow类 客户端调用克隆 深拷贝 1.Cow类也实现Cloneable接口 Sheep类的clone再添加调用cow的clone 客户端调用 1.Cow类实现序列化接口,不必实现Cloneable接口了 2.在Sheep类实现序列化接口 3.客户端调用 总结 引例 问题: 现在有一只羊(包含属性:名字Dolly.年龄2),需要克隆10只属性完全相同的羊. 一般解

Java之dao模式详解及代码示例

什么是dao模式? DAO(Data Access Object)顾名思义是一个为数据库或其他持久化机制提供了抽象接口的对象,在不暴露底层持久化方案实现细节的前提下提供了各种数据访问操作.在实际的开发中,应该将所有对数据源的访问操作进行抽象化后封装在一个公共API中.用程序设计语言来说,就是建立一个接口,接口中定义了此应用程序中将会用到的所有事务方法.在这个应用程序中,当需要和数据源进行交互的时候则使用这个接口,并且编写一个单独的类来实现这个接口,在逻辑上该类对应一个特定的数据存储.DAO模式实

深入理解JavaScript系列(42):设计模式之原型模式详解

介绍 原型模式(prototype)是指用原型实例指向创建对象的种类,并且通过拷贝这些原型创建新的对象. 正文 对于原型模式,我们可以利用JavaScript特有的原型继承特性去创建对象的方式,也就是创建的一个对象作为另外一个对象的prototype属性值.原型对象本身就是有效地利用了每个构造器创建的对象,例如,如果一个构造函数的原型包含了一个name属性(见后面的例子),那通过这个构造函数创建的对象都会有这个属性. 在现有的文献里查看原型模式的定义,没有针对JavaScript的,你可能发现很

JAVA 静态代理模式详解及实例应用

JAVA 静态代理模式 代理模式(Proxy):为其他对象提供一种代理以控制对这个对象的访问. 代理模式说白了就是"真实对象"的代表,在访问对象时引入一定程度的间接性,因为这种间接性可以附加多种用途. 在这实现代码之前,先讲一个简单的生活故事,我们都知道我们周边有很多公司有房屋买卖或租赁的业务,比如链家(LianJia),但链家本身是不存在任何实际房屋资产的,他所售卖租赁的房屋均需要房屋产权所有人(HomeMaster)提供,才得以实现公司的房源需求:同时公司要的卖房租房业务均需要公司

Java设计模式之单例模式详解

单例模式是非常常见的设计模式,其含义也很简单,一个类给外部提供一个唯一的实例.下文所有的代码均在github 源码整个项目不仅仅有设计模式,还有其他JavaSE知识点,欢迎Star,Fork 单例模式的UML图 单例模式的关键点 通过上面的UML图,我们可以看出单例模式的特点如下: 1.构造器是私有的,不允许外部的类调用构造器 2.提供一个供外部访问的方法,该方法返回单例类的实例 如何实现单例模式 上面已经给出了单例模式的关键点,我们的实现只需要满足上面2点即可.但是正因为单例模式的实现方式比较

java多线程Thread-per-Message模式详解

Thread-per-Message模式(这项工作就交给你了) 当你很忙碌的时候,这个时候公司楼下有个快递,于是你委托你的同事帮你拿一下你的快递,这样你就可以继续做自己的工作了 在Thread-Per-Message模式中,消息的委托端和执行端是不同的线程,消息的委托端会告诉执行端线程,这个工作就交给你了 Host类: 针对请求创建线程的类,主要通过开启新的线程,调用helper的handle,并将要打印的文字传递. public class Host { private final Helpe

详解Java设计模式——迭代器模式

迭代子模式 顾名思义,迭代器模式就是顺序访问聚集中的对象,一般来说,集合中非常常见,如果对集合类比较熟悉的话,理解本模式会十分轻松.这句话包含两层意思:一是需要遍历的对象,即聚集对象,二是迭代器对象,用于对聚集对象进行遍历访问.我们看下关系图: 这个思路和我们常用的一模一样,MyCollection中定义了集合的一些操作,MyIterator中定义了一系列迭代操作,且持有Collection实例,我们来看看实现代码: 两个接口: public interface Collection { pub

详解Java设计模式——命令模式

命令模式 命令模式很好理解,举个例子,司令员下令让士兵去干件事情,从整个事情的角度来考虑,司令员的作用是,发出口令,口令经过传递,传到了士兵耳朵里,士兵去执行.这个过程好在,三者相互解耦,任何一方都不用去依赖其他人,只需要做好自己的事儿就行,司令员要的是结果,不会去关注到底士兵是怎么实现的.我们看看关系图: Invoker是调用者(司令员),Receiver是被调用者(士兵),MyCommand是命令,实现了Command接口,持有接收对象,看实现代码: public interface Com

Java使用原型模式展现每日生活应用案例详解

本文实例讲述了Java使用原型模式展现每日生活.分享给大家供大家参考,具体如下: 一.模式定义 用原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象. 二.模式举例 1 模式分析 我们借用每日上班情景耒说明这一模式. 2 故事情节分析图 3 原型模式静态建模 4 代码示例 4.1 原型建立 package com.prototype.pojo; /** * 日常生活类 * * @author * */ public class DayLife implements Cloneable

Java动态代理(设计模式)代码详解

基础:需要具备面向对象设计思想,多态的思想,反射的思想: Java动态代理机制的出现,使得Java开发人员不用手工编写代理类,只要简单地指定一组接口及委托类对象,便能动态地获得代理类.代理类会负责将所有的方法调用分派到委托对象上反射执行,在分派执行的过程中,开发人员还可以按需调整委托类对象及其功能,这是一套非常灵活有弹性的代理框架.通过阅读本文,读者将会对Java动态代理机制有更加深入的理解.本文首先从Java动态代理的运行机制和特点出发,对其代码进行了分析,推演了动态生成类的内部实现. 代理模

Java中的代理模式详解及实例代码

java 代理模式详解 前言: 在某些情况下,一个客户不想或者不能直接引用一个对象,此时可以通过一个称之为"代理"的第三者来实现间接引用.代理对象可以在客户端和目标对象之间起到 中介的作用,并且可以通过代理对象去掉客户不能看到 的内容和服务或者添加客户需要的额外服务. 简单来说代理模式就是通过一个代理对象去访问一个实际对象,并且可以像装饰模式一样给对象添加一些功能. 静态代理 所谓静态代理即在程序运行前代理类就已经存在,也就是说我们编写代码的时候就已经把代理类的代码写好了,而动态代理则