装饰者模式

简介

装饰者模式的主要功能就是对一个类的功能进行扩充!

对于需要对某个类扩充,但是该类是final类,不能被继承,这是时候可以用装饰者模式来实现。

要实现装饰者模式,注意一下几点内容:

  • 1.装饰者类要实现真实类同样的接口

  • 2.装饰者类内有一个真实对象的引用(可以通过装饰者类的构造器传入)

  • 3.装饰类对象在主类中接受请求,将请求发送给真实的对象(相当于已经将引用传递到了装饰类的真实对象)

  • 4.装饰者可以在传入真实对象后,增加一些附加功能(因为装饰对象和真实对象都有同样的方法,装饰对象可以添加一定操作在调用真实对象的方法,或者先调用真实对象的方法,再添加自己的方法)

  • 5.不用继承

做馒头实例

1、如果有一个做馒头的接口IBread,定义了做馒头的标准。

2、做正常馒头需要实现这个接口,实现里面的方法。

3、我们有个要做“甜蜜素”的甜馒头需求,就需要对做正常馒头的类的功能加强。

IBread接口

做馒头的接口,包括准备材料,和面,蒸馒头,加工馒头(即调用前面三个步骤)

/**
* 定于做馒头的接口
*/
public interface IBread {
//准备材料
void prepare();
//和面
void kneadFlour();
//蒸馒头
void steamed();
//加工馒头(即调用前面三个步骤)
void process();
}

NormalBread类,做正常馒头

/**
* 正常做馒头
*/
public class NormalBread implements IBread { // 准备材料
@Override
public void prepare() {
System.out.println("准备面粉,水以及发酵粉...");
}
// 和面
@Override
public void kneadFlour() {
System.out.println("和面...");
} // 蒸馒头
@Override
public void steamed() {
System.out.println("蒸馒头...香喷喷的馒头出炉了");
} // 加工馒头(即调用前面三个步骤)
@Override
public void process() {
prepare();
kneadFlour();
steamed();
}
}

AbstractBread抽象类

定义制作馒头的抽象类,不做具体实现,只是调用IBread接口的方法

/**
* 定义制作馒头的抽象类
*/
public abstract class AbstractBread implements IBread { private final IBread bread; public AbstractBread(IBread bread) {
super();
this.bread = bread;
} @Override
public void prepare() {
this.bread.prepare();
}
@Override
public void kneadFlour() {
this.bread.kneadFlour();
}
@Override
public void steamed() {
this.bread.steamed();
} @Override
public void process() {
prepare();
kneadFlour();
steamed();
}
}

SweetDecorator类

增强的普通馒头的甜馒头类

public class SweetDecorator extends AbstractBread {

    public SweetDecorator(IBread bread) {
super(bread);
} public void paint(){
System.out.println("添加甜蜜素...");
} @Override
public void kneadFlour() {
//添加甜蜜素后和面
this.paint();
super.kneadFlour();
} }

TestDemo测试类

通过创建装饰类(SweetDecorator),传入未增强的对象(NormalBread)来实现对象的功能(方法)增强。

public class TestDemo {
public static void main(String[] args) { //生产正常的馒头
System.out.println("【开始】生产正常的馒头");
NormalBread normalBread = new NormalBread();
normalBread.process();
System.out.println("【结束】生产正常的馒头\n\n"); //生产含有甜蜜素的"甜馒头"
System.out.println("【开始】生产甜馒头");
IBread sweetDBread = new SweetDecorator(normalBread);
sweetDBread.process();
System.out.println("【结束】生产甜馒头");
}
}

打印结果

【开始】生产正常的馒头
准备面粉,水以及发酵粉...
和面...
蒸馒头...香喷喷的馒头出炉了
【结束】生产正常的馒头 【开始】生产甜馒头
准备面粉,水以及发酵粉...
添加甜蜜素...
和面...
蒸馒头...香喷喷的馒头出炉了
【结束】生产甜馒头

生产汽车实例

现在有一个生产汽车的接口,定义了汽车的标准(启动、运行、停止)。

Google汽车对这个接口进行了实现,并定义成了final类(不让别人动我的东西)。

然后需求来了,我们需要对这个Google汽车的功能进行增强,但是发现Google这个final类,不能被继承,这个时候就需要用到装饰者模式。

我们需要定义一个装饰类,这个类实现了Google汽车实现的接口,在这个装饰类中定义了一个Google汽车类的对象,在装饰类实例化的时候传入一个Google汽车类。有这个汽车类在实现的接口中调用方法,在需要增强的方法中再修改。

ICar接口

生产汽车的接口

/**
* 生产汽车的接口
*/
public interface ICar {
void start();
void run();
void stop();
}

GoogleCar实现的类

//相当于mysql驱动包,谷歌汽车开发人员实现类
public final class GoogleCar implements ICar{ @Override
public void start() { System.out.println("控制谷歌的汽车启动");
//调用谷歌汽车提供的C语言函数
} @Override
public void run() {
System.out.println("控制谷歌的汽车运行"); } @Override
public void stop() {
System.out.println("控制谷歌的汽车停止");
} }

增强汽车MyCar类

public class MyCar implements ICar{

	ICar car;

	public MyCar(ICar car) {
this.car=car;
} @Override
public void start() {
System.out.println("检查天气是否良好");
System.out.println("检查路况是否拥堵");
car.start();
} @Override
public void run() {
car.run();
} @Override
public void stop() {
car.stop();
} }

测试类

public class TestCar {
public static void main(String[] args) {
ICar car=new MyCar(new GoogleCar());
car.start();
car.run();
car.stop();
}
}

运行结果

检查天气是否良好
检查路况是否拥堵
控制谷歌的汽车启动
控制谷歌的汽车运行
控制谷歌的汽车停止

常见使用

  • Java 中的 InputStream 和 OutputStream 就是一个常见的装饰者模式的例子,通过不同的装饰者(如BufferedInputStream、DataInputStream)来增强基本的输入输出流功能。

  • Spring 框架中,AOP(Aspect-Oriented Programming)的实现机制就可以看作是一种装饰者模式,通过动态代理和切面(Aspect)来在不修改原有代码的基础上增加功能。

在这些例子中,都能看到装饰者模式的核心思想,即通过组合而非继承的方式,对对象的功能进行动态扩展。

BufferedOutputStreamOutputStream 的装饰者,通过在原始输出流上添加缓冲功能来提高性能。

// 创建原始的 OutputStream
OutputStream fileOutputStream = new FileOutputStream("example.txt"); // 使用装饰者 BufferedOutputStream 对原始流进行装饰
OutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream); // 使用 BufferedOutputStream 写入数据
String message = "Hello, Decorator Pattern!";
bufferedOutputStream.write(message.getBytes()); // 关闭流
bufferedOutputStream.close();

Java编程--设计模式之装饰者模式的更多相关文章

  1. Java 的设计模式之一装饰者模式

    刚开始接触装饰者的设计模式,感觉挺难理解的,不够后来花了一个晚上的时间,终于有头绪了 装饰者设计模式:如果想对已经存在的对象进行装饰,那么就定义一个类,在类中对已经有的对象进行功能的增强或添加另外的行 ...

  2. JAVA基础——设计模式之装饰者模式

    装饰模式 : 对新房进行装修并没有改变房屋的本质,但它可以让房子变得更漂亮.更温馨.更实用.    在软件设计中,对已有对象(新房)的功能进行扩展(装修).    把通用功能封装在装饰器中,用到的地方 ...

  3. Java 设计模式泛谈&装饰者模式和单例模式

    设计模式(Design Pattern) 1.是一套被反复使用.多人知晓的,经过分类编目 的 代码设计经验总结.使用设计模式是为了可重用代码,让代码更容易维护以及扩展. 2.简单的讲:所谓模式就是得到 ...

  4. Java设计模式 - - 单例模式 装饰者模式

    Java设计模式 单例模式 装饰者模式 作者 : Stanley 罗昊 [转载请注明出处和署名,谢谢!] 静态代理模式:https://www.cnblogs.com/StanleyBlogs/p/1 ...

  5. Java设计模式之装饰器模式

    1.装饰器模式的定义(保持接口,扩展功能) Decorate装饰器,顾名思义,就是动态的给一个对象添加一些额外的职责,就好比对房子进行装修一样. 2.装饰器模式的特征 具有一个装饰对象. 必须拥有与被 ...

  6. 设计模式之装饰者模式-java实例

    设计模式之装饰者模式 需求场景 我们有了别人提供的产品,但是别人提供的产品对我们来说还不够完善,我们需要对这个产品的功能进行补强,此时可以考虑使用装饰者模式. 我们已经有了产品,而且这个产品的功能非常 ...

  7. 实践GoF的23种设计模式:装饰者模式

    摘要:装饰者模式通过组合的方式,提供了能够动态地给对象/模块扩展新功能的能力.理论上,只要没有限制,它可以一直把功能叠加下去,具有很高的灵活性. 本文分享自华为云社区<[Go实现]实践GoF的2 ...

  8. Java IO流以及装饰器模式在其上的运用

    流概述 Java中,流是一种有序的字节序列,可以有任意的长度.从应用流向目的地称为输出流,从目的地流向应用称为输入流. Java的流族谱 Java的 java.io 包中囊括了整个流的家族,输出流和输 ...

  9. [转载]Java中继承、装饰者模式和代理模式的区别

    [转载]Java中继承.装饰者模式和代理模式的区别 这是我在学Java Web时穿插学习Java设计模式的笔记 我就不转载原文了,直接指路好了: 装饰者模式和继承的区别: https://blog.c ...

  10. C#设计模式(9)——装饰者模式(Decorator Pattern)

    一.引言 在软件开发中,我们经常想要对一类对象添加不同的功能,例如要给手机添加贴膜,手机挂件,手机外壳等,如果此时利用继承来实现的话,就需要定义无数的类,如StickerPhone(贴膜是手机类).A ...

随机推荐

  1. FLink处理函数ProcessFunction、KeyedProcessFunction、ProcessWindowFunction、 ProcessAllWindowFunction

    一.处理函数简介 在底层,我们可以不定义任何具体的算子(比如 map,filter,或者 window),而只是提炼出一个统一的"处理"(process)操作--它是所有转换算子的 ...

  2. FreeSql学习笔记——10.贪婪加载

    前言   FreeSql贪婪加载主要对应导航属性,将需要的数据一次加载出来,包括查询表的子表或者关联表的关联数据,用于一对一.一对多.多对一.多对多的关系数据查询,查询的时候一对一.多对一关系查询是可 ...

  3. 《刚刚问世》系列初窥篇-Java+Playwright自动化测试-15- iframe操作--番外篇 (详细教程)

    1.简介 通过前边三篇的学习,想必大家已经对iframe有了一定的认识和了解,今天这一篇主要是对iframe的一些特殊情况的介绍和讲解,主要从iframe的定位.监听事件和执行js脚本三个方面进行展开 ...

  4. 通过 fork 为项目做出贡献

    本文旨在帮助新手小伙伴了解学习如何参与 GitHub 项目,为其献上自己的一份力,留下属于自己的足迹. 普遍流程 通过 fork 为项目做出贡献一个普遍的流程如下图: sequenceDiagram ...

  5. [ZJOI2015]幻想乡战略游戏 题解

    题目链接:\(luogu\) 声明变量: \(tr1/tr2\):原树/点分树,用链式前向星维护 求链长(包括求 \(lca\)) \(a_i\):原树欧拉序 \(st_{i,j}\):\(RMQ\) ...

  6. 2024电子取证“獬豸杯”WP

    简介: 竞赛为个人赛,工具自备,只发证书(还没用,公告这么写的哈)竞赛选手们将对模拟的案件进行电子数据调查取证,全面检验参赛选手电子数据取证的综合素质和能力. 检材链接: https://pan.ba ...

  7. Ansible - [07] 定义变量的几种方式

    题记部分 Ansible 支持十几种定义变量的方式 Inventory 变量 Host Facts 变量 Register 变量 Playbook 变量 Playbook 提示变量 变量文件 命令行变 ...

  8. DW - 问题

    数据库三范式 1NF(First Normal Form):一个关系模式符合 1NF 的定义,则该关系模式是简单的.简单的意思就是不存在从属或重复的属性,即每个属性都是原子性的. 2NF(Second ...

  9. Manus爆火,是硬核还是营销?

    相信这两天小伙伴们应该被Manus刷屏了,铺天盖地的体验解读文章接踵而来,比如「数字生命卡兹克」凌晨爆肝的热文:「一手体验首款通用Agent产品Manus」.从公众号.朋友圈.抖音.央媒,都能看到Ma ...

  10. 更新docker配置,重启docker进程,容器不重启

    前言 想重启 dockerd ,重新加载配置文件,可又怕重启容器,影响线上业务. reload 重新加载配置 dockerd reload 配置,不会重启 dockerd kill -SIGHUP $ ...