java经典设计模式4,JAVA设计模式(4) 之装饰设计模式

在现实生活中我们的汽车都具备跑的功能,我们可以不改变汽车原有功能的前提下,把它放入一个装修厂,开进去让里面给咱们的车子做一些装饰,开出来之后呢,就具备了上天的功能了(技术可达是可以的哈),这就给原来的汽车对象,增加了额外的功能。

再举一个例子:假设我们非常爱惜一张照片,我们可以不改变照片本身前提下,给它增加一个相框,使得它具有防潮的功能,而且用户可以根据需要给它增加不同类型的相框,甚至可以在一个小相框的外面再套一个大相框。这就是针对照片这个对象的装饰,在软件工程中,同样存在类似的功能,使用装饰模式可以透明的增加指定对象的功能。

1、装饰模式的引入

首先咱们来看一段代码:

假设我要设计一个汽车类,然后在里面定义了汽车可能存在的功能(后面我还要扩展汽车的功能):

public class Car {

public void run(){

System.out.println("能跑");

}

public void fly(){

System.out.println("能飞");

}

public void sweep(){

System.out.println("能游");

}

public void show(){

System.out.println("该汽车拥有的功能:");

this.run();

this.fly();

this.sweep();

}

}

public class Main {

public static void main(String[] args) {

Car bus = new Car();

bus.show();

}

}

这段代码并没有什么设计可言的。运行结果就不贴出来了,可见,我们在客户端造了一个巴士,调用了巴士的 show 方法后,发现有一些功能并不是巴士的(飞、游泳),这样显然是存在问题的。那么我们可能会作如下修改:

使用继承,每个继承体系归类。

//接口可以改为抽象类

public interface Car {

//只要是有汽车,都具备跑的功能

void run();

//调用展示该汽车存在的功能

void show();

}

public class RunCar implements Car{

//普通汽车只具备跑的功能

public void run() {

System.out.println("可以跑");

}

public void show() {

this.run();

}

}

public class FlyCar implements Car {//扩展的汽车具备飞的功能

@Override

public void run() {

System.out.println("可以跑");

}

// 定义自己的功能

public void fly() {

System.out.println("可以飞");

}

@Override

public void show() {

this.run();

this.fly();

}

}

public class SwimCar implements Car{//扩展的汽车具备游泳的功能

@Override

public void run() {

System.out.println("可以跑");

}

public void swim(){

System.out.println("可以游泳");

}

@Override

public void show() {

this.run();

this.swim();

}

}

然后在客户端调用:

public class Main {

public static void main(String[] args) {

Car bus = new RunCar();

bus.show();

Car flyCar = new FlyCar();

flyCar.show();

}

}

运行结果如下:

ab9d20c23869226e15ad7174d6662d44.png

我们这里使用继承的方式来扩展系统(Car)的功能,这样拥有了设计可言,但是还是存在问题的。问题在于如果增加子类,他拥有“遁地”的功能的话(当然技术先进可以做到哈,不要在意这些细节),仍然要在遁地这个子类里面定义额外的“遁地”方法。这个时候,为了解决这种继承扩展功能问题,就引入了本节的内容——装饰模式。装饰模式是扩展系统功能的最佳选择。装饰模式是对已有对象的功能进行扩展(装修、装饰),以获得更加符合用户需求的对象,使得对象具有更加强大的功能。

2、装饰模式概述

装饰模式可以在不改变一个对象本身功能的基础上给对象增加额外的新行为,在现实生活中,这种情况也到处存在,例如装修窗户,我们可以不改变窗户本身,给它增加一些额外的装饰(比如窗花),增加他的可观赏性,而且用户可以根据需要给它增加不同类型的窗花,甚至可以装饰多层。

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

装饰模式定义如下:

动态地给一个对象增加一些额外的职责,就增加对象功能来说,装饰模式比生成子类实现更为灵活。装饰模式是一种对象结构型模式。

在装饰模式中,为了让系统具有更好的灵活性和可扩展性,我们通常会定义一个抽象装饰类,而将具体的装饰类作为它的子类,装饰模式结构如图所示:

82c07b801f1a6a93e6888c1f65326caf.png

在装饰模式结构图中包含如下几个角色:

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

ConcreteComponent(具体构件):它是抽象构件类的子类,用于定义具体的构件对象,实现了在抽象构件中声明的方法,装饰器可以给它增加额外的职责(方法)。【上述 RunCar 就是这个角色】

Decorator(抽象装饰类):它也是抽象构件类的子类,用于给具体构件增加职责,但是具体职责在其子类中实现。它维护一个指向抽象构件对象的引用,通过该引用可以调用装饰之前构件对象的方法,并通过其子类扩展该方法,以达到装饰的目的。比如针对开头的案例,它可以对 Car 增加除了跑额外的功能 “可以游泳”、“下水”、“遁地”等。

ConcreteDecorator(具体装饰类):它是抽象装饰类的子类,负责向构件添加新的职责。每一个具体装饰类都定义了一些新的行为,它可以调用在抽象装饰类中定义的方法,并可以增加新的方法用以扩充对象的行为。【上述 FlyCar 和 SwinCar 可以作为整个角色对 Car 动能进行扩展】

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

装饰模式的核心在于抽象装饰类的设计,其典型代码如下所示:

class Decorator implements Component{

//持有抽象构件的引用

private Component component;

//注入一个抽象构件类型的对象(依赖倒置)

public Decorator(Component component) {

this.component=component;

}

public void operation(){

//调用原有业务方法

component.operation();

}

}

在抽象装饰类 Decorator 中定义了一个 Component 类型的对象 component,维持一个对抽象构件对象的引用,并可以通过构造方法或 Setter 方法将一个 Component 类型的对象注入进来,同时由于 Decorator 类实现了抽象构件 Component 接口,因此需要实现在其中声明的业务方法 operation(),需要注意的是在 Decorator 中并未真正实现 operation() 方法,而只是调用原有 component 对象的 operation() 方法,它没有真正实施装饰,而是提供一个统一的接口,将具体装饰过程交给子类完成。

在 Decorator 的子类即具体装饰类中将继承 operation() 方法并根据需要进行扩展,典型的具体装饰类代码如下:

class ConcreteDecorator extends Decorator{

public ConcreteDecorator(Component component){

super(component);

}

public void operation(){

super.operation(); //调用原有业务方法

addedBehavior(); //调用新增业务方法

}

//新增业务方法

public void addedBehavior(){

……

}

}

在具体装饰类中可以调用到抽象装饰类的 operation() 方法,同时可以定义新的业务方法,如 addedBehavior()。

由于在抽象装饰类 Decorator 中注入的是 Component 类型的对象,因此我们可以将一个具体构件对象注入其中,再通过具体装饰类来进行装饰;此外,我们还可以将一个已经装饰过的 Decorator 子类的对象再注入其中进行多次装饰,从而对原有功能的多次扩展。

3、装饰模式实战

针对文章开头处的案例,使用装饰设计模式进行修改。

对于装饰模式,可以难于理解的地方在于 Decorator 抽象装饰类为何会继承或者实现 Component 抽象构建类。如果我们不继承 Component 构建类使用装饰模式的时候,代码如下:

UML图:

7c06c077da7d6ce803b1aa3c7bda55da.png

代码:

```java

//Component抽象构件角色

public interface Car {

//只要是有汽车,都具备跑的功能

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/382985.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

matlab双纵轴刻度覆盖问题,求助: matlab双纵轴换图问题

非常感谢!不过,y2的范围是-1*(10^6),7*(10^6),但是不等分:(把y1的范围调成0.4:0.2:5.8也有28个元素,然后y2也有28个元素,但是不等分,是个曲线,而且,这个曲线的值与y1是有关的。数据见附件syms d…

php最新图片漏洞,2018最新PHP漏洞利用技巧

本文学习了几种新式的php exploit方法,在此做一笔记文件删除漏洞, unlink()Phar 反序列化, file*()PHP对象实例化, ReflectionClass()0x01 WordPress Design Flaw Leads to WooCommerce RCEWooCommerce 3.4.6本版本之前存在任意删除漏洞,因为WordPress的…

php过气了吗,留几手 留几手过气原因

1、很多时候,人们做事情只是为了自己,没有任何理由,没有任何结果,只是为了满足一些内心的期望。2、太理智的人,往往爱到一半,本能地退却。唯一突出的是他的JB。3、怎样才能自由地睡去女文艺青年&#xff1f…

matlab频率阻抗,有分析阻抗的matlab脚本吗?

以上来自于谷歌翻译以下为原文Interesting...- You cross-posted to two forums. I have deleted the other post.- You dont indicate what scope you are using or what you have tried.Most Keysight (and Agilent) scopes have an FFT or Spectrum function available. Hav…

php中修改弹窗的样式,CSS变形弹窗效果示例

大家都知道,弹出窗体已经是现在网页常用的一种交互设计,在这个注重交互动画体验的时代,网页弹窗也是可以来点新鲜点子的,比如今天分享的CSS 变形Modal Window。当用户点击按钮时,按钮将会变成一个全屏的屏幕&#xff0…

次梯度法matlab代码,实例:连续化次梯度法解 LASSO 问题

实例:连续化次梯度法解 LASSO 问题我们将在此页面中构造一个 LASSO 问题并且展示连续化次梯度方法在其中的应用。目录构造LASSO优化问题设定随机种子。clear;seed 97006855;ss RandStream(mt19937ar,Seed,seed);RandStream.setGlobalStream(ss);构造 LASSO 优化问…

qq ip探测仪 php,巧用Win7资源监视器,查看QQ好友IP

用QQ时间比较长、喜欢DIY的朋友都知道,有一些第三方版本的QQ或者插件可以显示好友IP地址,但其实在Windows7中根本用不着第三方软件,在系统自带的资源监视器中,就能很方便的看到QQ好友的IP地址。首先,打开“任务管理器”…

oracle安装显示注册表,windows下oracle 11g r2 安装过程与卸载详细图解

Oracle 11g安装1.解压下载的包,然后进入包内,点击setup.exe开始安装 。2.出现如下:一般把那个小对勾取消,点击下一步进行,弹出下图这个后点‘是3.下图后,选择创建和配置数据库,点击下一步。4.下…

php一行多个商品,【后端开发】php一行展示多个商品怎么实现

php一行展示多个商品怎么实现php可以用来连接数据库查询商品,并输出展示给用户,但想要实现一行展示多个商品需要用到css技术,具体实现如下:1、首先php代码$sql "select * from user";$result $conn->query($sql);i…

linux双网卡端口聚合,Linux双网卡聚合改造

Linux双网卡聚合改造一、环境和需求Linux主机只有一块网卡接到交换机上,为了消除交换机的单点,新增一台交换机,Linux主机端新接一块网卡到新交换机上,对这两块网卡做聚合达到目的。二、物理连线使用网线连接新增交换机和新网卡三、…

linux服务器无法识别xml文件,linux上重启服务器提示找不到smartbi-config.xml文件

(本文档仅供参考)问题服务器上重启服务时,报了如下错误信息:(备注:因安全考虑,新版本的config登录界面已经不展示config文件的加载路径了。)解决方案原因一:这是一个关于在哪个路径下启动smartbi服务的问题。首先&…

linux堆上的内存可执行吗,pwn的艺术浅谈(二):linux堆相关

这是linux pwn系列的第二篇文章,前面一篇文章我们已经介绍了栈的基本结构和栈溢出的利用方式,堆漏洞的成因和利用方法与栈比起来更加复杂,为此,我们这篇文章以shellphish的how2heap为例,主要介绍linux堆的相关数据结构…

manjaro linux下载软件,manjaro linux

manjaro linux下载。manjaro linux是基于Arch Linux开发的Linux操作系统!对于裸服务器、虚拟机、IaaS 和 PaaS 方面都得到了加强,而且内置了强大的数据中心满足商业的各种要求,是强大的混合云平台和物理系统!manjaro linux介绍man…

linux 安装qt 4.6软件,QT学习之一:Linux下安装QT之版本qt-4.6.3

在Linux中分别安装应用于不同平台的QT:PC;嵌入式X86;ARM。这三者PC版、嵌入式X86版和ARM版的区别主要体现在:当configure时分别加了不同的参数,具体区别是:PC平台:在linux中全安装qt&#xff0c…

linux kill命令使用方法,Linux初学者的killall命令(8个例子)

Linux初学者的killall命令(8个例子)我们已经讨论了kill命令 ,如果你想在Linux中终止进程,你可以使用kill命令 。 但是,还有一个命令行实用程序可以用于相同的目的: killall 。 在本教程中,我们将使用一些易于理解的示例…

c语言 字符串 url,如何对URL字符串进行百分号编码

在和web服务进行交互时,我们经常需要对URL中的特定字符和传输的表单数据进行百分号编码。例如,’&’在百分号编码时会变成’%26’。搞清楚 URL中哪部分的哪些字符应该进行百分号编码了并不是件易事。最好的资料好像是RFC 3986和W3C HTML5。出于兴趣和…

击鼓传花击鼓次数相同c语言,JavaScript 实现击鼓传花游戏

大家小时候应该都玩过击鼓传花(Hot Potato)的游戏吧!一群小孩围成一个圆圈,把花尽快的传给旁边的人。某一时刻传花停止,这时花在谁手里,谁就退出圆圈结束游戏。重复此过程,直到剩下最后一个孩子,即为胜者。…

前端的c语言面试题,前端工程师面试题汇总(选择题)

前端工程师面试题汇总(选择题)时间:2017-12-05 来源:前端工程师面试题推荐作为一名前端工程师,我们必不可少的就是参加面试,面试过程中会遇到各类奇葩的问题,今天小编为大家汇总了一些相关的问题,希望可…

android 磁场传感器 罗盘,Android开发获取重力加速度和磁场强度的方法

本文实例讲述了Android开发获取重力加速度和磁场强度的方法。分享给大家供大家参考,具体如下:Android获取重力加速度和磁场强度主要依靠:Sensor.getRotationMatrix (float[] R, float[] I, float[] gravity, float[] geomagnetic)输入数据&am…

html在线编辑器 asp.net,ASP.NET网站使用Kindeditor富文本编辑器配置步骤

1. 下载编辑器下载 KindEditor 最新版本,下载页面: http://www.kindsoft.net/down.php2. 部署编辑器解压 kindeditor-x.x.x.zip 文件,将editor文件夹复制到web目录下3、在网页中加入(ValidateRequest"false")4、引入脚本文件(XXX部分需要修改)…