首页 > 编程语言 >【Java设计模式】装饰者设计模式

【Java设计模式】装饰者设计模式

时间:2023-04-26 18:57:49浏览次数:93  
标签:Java public Beverage 抽象 构件 设计模式 装饰 beverage

目录

1. 装饰者模式(Wrapper)概念

​ 动态地给一个对象增加一些额外的职责(增强),增加对象功能来说,装饰模式比生成子类实现更为灵活。装饰模式是一种对象结构型模式。在装饰者模式中,为了让系统具有更好的灵活性和可扩展性,我们通常会定义一个抽象装饰类(Decorator),而将具体的装饰类作为它的子类。。从某种程度上讲,装饰器非常类似于“继承”,它们都是为了增强原始对象的功能,区别在于方式的不同

亦称:装饰者模式、装饰器模式、Wrapper、Decorator

  • 继承:是在编译时(compile-time)静态地通过对原始类的继承完成
  • 装饰者:是在程序运行时(run-time)通过对原始对象动态地“包装”完成

2. 装饰者是什么?

​ 在不必改变原类文件和原类使用的继承的情况下,动态地扩展一个对象的功能。就增加对象功能来说,装饰模式比生成子类实现更为灵活。装饰模式是一种对象结构型模式。

比如,我们已有的饮料类,扩展了一个新的品种,可以使用装饰者模式,无需用原始的增加子类去继承饮料父类的方式。

3. 装饰者设计模式- 参与者

3.1 组成

  • Component(抽象构件):它是具体构件和抽象装饰类的共同父类【顶级父类】,声明了在具体构件中实现的业务方法,它的引入可以使客户端以一致的方式处理未被装饰的对象以及装饰之后的对象,实现客户端的透明操作。

  • ConcreteComponent(具体构件):它是抽象构件类的子类,用于定义具体的构件对象,实现了在抽象构件中声明的方法,装饰器可以给它增加额外的职责(方法)。

  • Decorator(抽象装饰类):它也是抽象构件类的子类,用于给具体构件增加职责,但是具体职责在其子类中实现。它维护一个指向抽象构件对象的引用,通过该引用可以调用装饰之前构件对象的方法,并通过其子类扩展该方法,以达到装饰的目的。

  • ConcreteDecorator(具体装饰类):它是抽象装饰类的子类,负责向构件添加新的职责。每一个具体装饰类都定义了一些新的行为,它可以调用在抽象装饰类中定义的方法,并可以增加新的方法用以扩充对象的行为。

由于具体构件类和装饰类都实现了相同的抽象构件接口,因此装饰模式以对客户透明的方式动态地给一个对象附加上更多的责任,换言之,客户端并不会觉得对象在装饰前和装饰后有什么不同。装饰模式可以在不需要创造更多子类的情况下,将对象的功能加以扩展。

3.1 Demo

1. Component(抽象构件)

创建一个 Beverage(饮料) 抽象类

public abstract class Beverage {

    // 每种饮料的描述:“名称,配料1,配料2...”
    String description = "Unknown Beverage";

    public String getDescription() {
        return description;
    }

    // 每种类型饮料的价格不同,故定义为抽象方法
    public abstract double cost();
}

2. ConcreteComponent(具体构件)

有几种饮料,比如咖啡、奶茶,它们都是饮料的一种所以继承Beverage类

public class DarkRoast extends Beverage {

    public DarkRoast() {
        description = "DarkRoast ";
    }

    @Override
    public double cost() {
        return 3.00;
    }
}
public class Decaf extends Beverage {

    public Decaf() {
        description = "Decaf";
    }

    @Override
    public double cost() {
        return 1.00;
    }
}

3. Decorator(抽象装饰类)

public abstract class CondimentDecorator extends Beverage {

    // 因为每种配料对饮料的描述进行了附加的说明,所以每种饮料添加配料后,都需要重写 getDescription方法。
    @Override
    public abstract String getDescription();

}

4. ConcreteDecorator(具体装饰类)

具体增加的装饰(增强)

public class Milk extends CondimentDecorator {

    // 关联了一种饮料,以便对附加新的行为
    Beverage beverage;

    public Milk(Beverage beverage) {
        this.beverage = beverage;
    }

    // 附加新的行为,描述
    @Override
    public String getDescription() {
        return beverage.getDescription() + ",Milk";
    }
  
    // 附加新的行为,价格
    @Override
    public double cost() {
        return beverage.cost() + 0.10;
    }
}
public class Mocha extends CondimentDecorator {

    // 关联了一种饮料,以便对附加新的行为
    Beverage beverage;

    public Mocha(Beverage beverage) {
        this.beverage = beverage;
    }

    // 附加新的行为,描述
    @Override
    public String getDescription() {
        return beverage.getDescription() + ",Mocha";
    }
  
    // 附加新的行为,价格
    @Override
    public double cost() {
        return beverage.cost() + 1.10;
    }
}

5. Test 类:

public class Test {
    public static void main(String[] args) {
      
        // 创建 Beverage 饮料,不加配料。
        Beverage espresso = new Espresso();
        System.out.println(espresso.getDescription() +" " + espresso.cost());

        // 创建 DarkRoast 饮料,加配料 Mocha、Mocha。
        Beverage darkRoast = new DarkRoast();
        darkRoast = new Mocha(darkRoast);
        darkRoast = new Milk(darkRoast);
        System.out.println(espresso.getDescription() +" " + espresso.cost());
    }

3.2 总结

  • 封装变化
  • 多用组合,少用继承
  • 针对接口编程,不针对实现编程
  • 为交互对象之间的松耦合设计而努力
  • 对扩展开放,对修改关闭(可扩展饮料、配料,而不修改原代码,这种场景下使用装饰者模式,体现了此原则)

4. JDK源码中的装饰者设计模式

InputStream为例子,InputStream有很多的实现类:

FileInputStream:实现文件的读取。

DataInputStream:读取各种基本数据类型的数据。

BufferedInputStream:可缓存的文件流。

ObjectInputStream:读取对象的文件流。

其他的实现还有很多很多,这里的实现就使用了装饰者模式,保证InputStream不变的前提下,增加其他功能。想象一下,如果要同时实现文件读取和可缓存,那么就可以这样写:

new BufferedInputStream(new FileInputStream(""));

如果想实现基本数据类型读取+文件读取,就可以这样写:

new DataInputStream(new FileInputStream(""));

InputStream是一个抽象类,定义了read方法,代码作了精简:

public abstract class InputStream implements Closeable{
    public abstract int read() throws IOException;
}

BufferedInputStream是InputStream子类的子类,在继承关系上,BufferedInputStream继承FilterInputStream,FilterInputStream继承InputStream,这里相当于对装饰类进行了再扩展,看一下FilterInputStream:

public class FilterInputStream extends InputStream{
    protected volatile InputStream in;
    protected FilterInputStream(InputStream in) {
        this.in = in;
    }
    public int read() throws IOException {
        return in.read();
    }
}

而BufferedInputStream以及DataInputStream都是对FilterInputStream再做一些功能上的增强,很巧妙的实现了在不必改变原类文件情况下,允许向一个现有的对象添加新的功能。

扩展:开闭原则

开放关闭原则:类应该对扩展开放,对修改关闭。

标签:Java,public,Beverage,抽象,构件,设计模式,装饰,beverage
From: https://www.cnblogs.com/lihw/p/17356986.html

相关文章

  • VScode 创建Java项目
    1.点击view找到命令行输入java(ctrl+shift+P)src:一般是自己写的源代码的文件lib:外部引用库等其他的内容bin:二进制程序,用以存放未来的编译文件等  ......
  • javascript操作cookie
    functionsetCookie(name,value){varDays=30;varexp=newDate();exp.setTime(exp.getTime()+Days*24*60*60*1000);document.cookie=name+"="+escape(value)+";expires="+exp.toGMTString();......
  • java 多线程的start()和run()的理解
    run()方法中是各个线程要执行的具体内容。所以当一个线程直接调用run()时那么直接开始执行方法体,这是在main线程中的多个线程只能时按照顺序的等待前面的线程结束run()方法的执行。而调用start方法只是线程进入准备阶段(Ready),并没有真正执行,这需要JVM进行分配时间片进行轮转线程执......
  • VScode配置Java环境
    1.安装VScode2.配置Java环境变量3.配置VScodeJava插件参考博文:VSCode配置调试编译java环境,史上最全!!!安装ExtensionPackforJava插件 VScode配置Java:home文件——首选项——设置输入javahome,选择在settings.json中编辑 {"window.zoomLevel":1,//"j......
  • JavaScript怎么判断一个host是否在局域网中?
    在JavaScript中,可以使用WebRTCAPI来判断一个host是否在局域网中。WebRTCAPI提供了一种用于在浏览器中进行实时通信的技术,其中包括了一些用于网络发现的API。下面是一个简单的实现:functioncheckLocalhost(callback){varpc=newRTCPeerConnection({iceServers:[]});pc......
  • 大华面试java 2023.5
    一张表随着业务递增,如何对一个字段进行快速查询。比如对身高字段查询,要求查询是10的倍数的。考虑分库分表,或者提前计算设置标志位加索引OOM的场景有哪些,分别是什么情况下会出现这样的问题项目中的复杂设计开发流程springcloud动态更新class的原理,类加载机制,java中类加载......
  • Java+proj4j实现根据EPSG编码进行坐标系转换
    场景Java+GeoTools实现WKT数据根据EPSG编码进行坐标系转换:https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/130370754上面使用GeoTools实现坐标系转换。Vue+Openlayers+proj4实现坐标系转换:https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/13038879......
  • linux下分析java程序占用CPU、内存过高
    转:https://www.cnblogs.com/wu-wu/p/11923250.html CPU过高分析1)使用TOP命令查看CPU、内存使用状态可以发现CPU占用主要分为两部分,一部分为系统内核空间占用CPU百分比,一部分为用户空间占用CPU百分比。其中CPU状态中标示id的为空闲CPU百分比。当空闲CPU百分比越低,说明CPU占用率......
  • java连接jdbc-mssql数据库
    packagecom.swift.erp;importjava.sql.SQLException;importjava.sql.Statement;importjava.sql.Connection;importjava.sql.DriverManager;importjava.sql.ResultSet;publicclassAPP{//引入sqljdbc.jarpublicstaticvoidmain(String[]args......
  • java excel带图片导入
    日常项目中使用easyExcel但是easyExcel不支持带图片的excel导入,后面通过网络查找,再加上询问技术经理终于解决,大前提是图片在excel中不是嵌入的,需要悬浮才可以读取。代码如下引用jar包为<!--需要2.10版本及以上不然excel缺少工具类--><dependency><groupId>com......