java设计模式之:装饰器模式

news/2024/11/24 6:17:29/

前言

在软件设计中,我们也有一种类似新房装修的技术可以对已有对象(新房)的功能进行扩展(装修),以获得更加符合用户需求的对象,使得对象具有更加强大的功能。这种技术对应于一种被称之为装饰模式的设计模式,本章将介绍用于扩展系统功能的装饰模式。

装饰器模式概述

装饰模式可以在不改变一个对象本身功能的基础上给对象增加额外的新行为,在现实生活中,这种情况也到处存在,例如一张照片,我们可以不改变照片本身,给它增加一个相框,使得它具有防潮的功能,而且用户可以根据需要给它增加不同类型的相框,甚至可以在一个小相框的外面再套一个大相框。

装饰模式是一种用于替代继承的技术,它通过一种无须定义子类的方式来给对象动态增加职责,使用对象之间的关联关系取代类之间的继承关系。在装饰模式中引入了装饰类,在装饰类中既可以调用待装饰的原有类的方法,还可以增加新的方法,以扩充原有类的功能。

装饰模式结构图:

image.png

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

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

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

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

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

简单案例

场景:天气太热了,喝点儿冰水解解暑;加点儿柠檬片,让果汁好喝点儿。

先定义一个喝水的接口

public interface Drink {/*** 喝水*/void drink();
}

写一个接口的实现

public class DrinkWater implements Drink {@Overridepublic void drink() {System.out.println("喝水");}}

一个简单的装饰器

public class DrinkDecorator implements Drink {private final Drink drink;public DrinkDecorator(Drink drink) {this.drink = drink;}@Overridepublic void drink() {System.out.println("先加点儿柠檬片");drink.drink();}}

测试

public class DrinkMain {public static void main(String[] args) {Drink drink = new DrinkWater();drink = new DrinkDecorator(drink);drink.drink();}
}

运行结果

先加点儿柠檬片
喝水

一个简单的装饰器模式例子就写完了,这里只是先了解一下装饰器模式。

案例场景

image.png
大家应该都喝过奶茶吧,我经常喝的原味奶茶,里面可以添加配料,每增加一种配料,该奶茶的价格就会增加,要求计算一种奶茶的价格。

下面就可以使用装饰器模式来模拟这种场景。

定义抽象构件类

/*** 奶茶*/
public interface MilkyTea {double cost();
}

定义具体构件类

/*** 珍珠奶茶*/
public class PearlMilkyTea implements MilkyTea{@Overridepublic double cost() {return 13.0;}
}

定义抽象装饰类

public abstract class MilkyTeaDecorator implements MilkyTea{private MilkyTea milkyTea;public MilkyTeaDecorator(MilkyTea milkyTea){this.milkyTea = milkyTea;}@Overridepublic double cost() {return milkyTea.cost();}
}

定义具体装饰类

/*** 椰果奶茶*/
public class CoconutDecorator extends MilkyTeaDecorator{public CoconutDecorator(MilkyTea milkyTea) {super(milkyTea);}@Overridepublic double cost() {System.out.println("添加椰果");return super.cost() + 2.0;}
}/*** 布丁奶茶*/
public class PuddingDecorator extends MilkyTeaDecorator{public PuddingDecorator(MilkyTea milkyTea) {super(milkyTea);}@Overridepublic double cost() {System.out.println("添加布丁");return super.cost() + 1.5;}
}

测试代码

public class Client {public static void main(String[] args) {MilkyTea milkyTea = new PearlMilkyTea();milkyTea = new CoconutDecorator(milkyTea);milkyTea = new PuddingDecorator(milkyTea);System.out.println(milkyTea.cost());}
}

运行结果

添加布丁
添加椰果
16.5

在客户端代码中,我们先定义了一个MilkyTea类型的具体构件对象milkyTea,然后将milkyTea作为构造函数的参数注入到具体装饰类CoconutDecorator中,得到一个装饰之后对象milkyTea,再将装饰了一次之后的milkyTea对象注入另一个装饰类PuddingDecorator中实现第二次装饰,得到一个经过两次装饰的对象milkyTea,再调用milkyTea的cost()方法即可得到一杯既加了布丁又加了椰果的珍珠奶茶价格。

装饰器模式优缺点

优点

  1. 装饰类和被装饰类可以独立发展,不会相互耦合。
  2. 相比于继承,更加的轻便、灵活。
  3. 可以动态扩展一个实现类的功能,不必修改原本代码

缺点

  1. 会产生很多的装饰类,增加了系统的复杂性。
  2. 这种比继承更加灵活机动的特性,也同时意味着装饰模式比继承易于出错,排错也很困难,对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为繁琐。

装饰器模式适用场景

  1. 在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。

  2. 当不能采用继承的方式对系统进行扩展或者采用继承不利于系统扩展和维护时可以使用装饰模式。不能采用继承的情况主要有两类:第一类是系统中存在大量独立的扩展,为支持每一种扩展或者扩展之间的组合将产生大量的子类,使得子类数目呈爆炸性增长;第二类是因为类已定义为不能被继承(如Java语言中的final类)。

总结

装饰模式降低了系统的耦合度,可以动态增加或删除对象的职责,并使得需要装饰的具体构件类和具体装饰类可以独立变化,以便增加新的具体构件类和具体装饰类。在软件开发中,装饰模式应用较为广泛,例如在JavaIO中的输入流和输出流的设计、javax.swing包中一些图形界面构件功能的增强等地方都运用了装饰模式。


http://www.ppmy.cn/news/340886.html

相关文章

有哪些好用抠图软件?这几种抠图工具简单又高效

有什么好用的抠图软件呢?通过抠图技术将不同的元素组合在一起,创造出独特的艺术效果。我们日常中也会经常需要进行照片抠图,如拍出的照片背景不满意,想要抠出图片中的人物放到新的背景中,这些都是需要进行抠图才能够完…

壁挂炉洗澡怎么调水温_壁挂炉水温太高怎么维修【调节壁挂炉温度】

夏季壁挂炉水温太高主要有两个方面: 第一、夏季自来水基础水温比冬季高,夏季一般在30度左右; 第二、主要问题在于夏季燃气压力太高。 解决办法,将壁挂炉燃气阀门关闭一半即可。在洗浴工作状态下,调节生活热水设定温度在35度左右&a…

解析kubernetes部署:微信配置文件部署

微信安全配置文件 以下两步二选一 一、暂时没有微信配置文件 1、创建configmap kubectlcreateconfigmapweixin-config--from-file/opt/kubernetes/weixin/weixin-mp.txt--namespacens-javashop 2、创建微信配置文件service(执行如下命令) kubectlcreate-f/opt/kubernetes/weix…

【国产复旦微FMQL45教程】-小试牛刀之LED

本教程采用 FMQL7045 FPGA开发板来完成整个试验,板卡照片如下: 具有丰富的接口资源,系统框图如下: 本教程用于完成基于Vivado的FMQL45的LED实验,目标是能够将这款开发板PL端先跑起来。 对于纯 PL 设计,我们…

linux中awd的详细使用方法,求教awdflash的使用方法,详细些,我是初學者。

一、分区备份 使用Ghost进行系统备份,有整个硬盘(Disk)和分区硬盘(Partition)两种方式。在菜单中点击 Local(本地)项,在右面弹出的菜单中有3个子项,其中 Disk表示备份整个硬盘(即克隆)、Partition 表示备份硬盘的单个分区、Check 表示检查硬盘…

计算机组装与维修blos,计算机组装与维护标准教程 设置bios参数.pptx

第11章 设置BIOS参数;11.1 BIOS/CMOS概述;11.1 BIOS/CMOS概述;11.1.2 BIOS的功能及启动顺序;11.1 BIOS的功能及启动顺序;;11.2 BIOS的分类;11.2 进入BIOS的方法;11.3 设置Award BIOS;10.2.2 标准CMOS设置;10.2.3 高级BIOS功能设定;10.2.4 高级芯片功能设定;10.2.5 集成外部设备…

计算机组装与维护要点,《计算机组装与维护》要点.ppt

《计算机组装与维护》要点.ppt 本课程知识结构 第6章 计算机的日常维护 6.3 注册表的使用 6.3.1 注册表的结构与组成 1.注册表的结构 如果需要使用注册表编辑器,可以执行“开始”→“运行”命令,在“运行”对话框的“打开”文本框中输入Regedit.exe或Reg…

服务器主板电池的电压不稳定怎么办,轻松解决CMOS掉电烦恼这绝招

机房中的电脑都没有安装软驱,按Del键进入CMOS,将软驱设置为“None”,保存退出后不再出现错误提示。但关机时间一长,再开机时又会出现相同的错误提示,而且进入Windows 98以后,系统时钟总是回到2001年&#x…