一、基本介绍

动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。

二、装饰器模式的结构

1、Component,抽象构件

Component是一个接口或者抽象类,是定义我们最核心的对象,也可以说是最原始的对象,比如街边小吃;

2、ConcreteComponent,具体构件,或者基础构件

ConcreteComponent是最核心、最原始、最基本的接口或抽象类Component的实现,可以单独用,也可将其进行装饰,比如街边小吃最有名的手抓饼;

3、Decorator,装饰角色

 一般是一个抽象类,继承自或实现Component,在它的属性里面有一个变量指向Component抽象构件,我觉得这是装饰器最关键的地方。

4、ConcreteDecorator,具体装饰角色

ConcreteDecoratorA和ConcreteDecoratorB是两个具体的装饰类,它们可以把基础构件装饰成新的东西,比如把一个普通的手抓饼装饰成加蛋、加肠儿、金针菇的手抓饼。

三、装饰器模式优缺点

1、优点

(1)装饰类和被装饰类可以独立发展,而不会相互耦合。换句话说,Component类无需知道Decorator类,Decorator类是从外部来扩展Component类的功能,而Decorator也不用知道具体的构件。

(2)装饰器模式是继承关系的一个替代方案。我们看装饰类Decorator,不管装饰多少层,返回的对象还是Component(因为Decorator本身就是继承自Component的),实现的还是is-a的关系。

2、缺点

(1)装饰器模式虽然减少了类的爆炸,但是在使用的时候,你就可能需要更多的对象来表示继承关系中的一个对象

(2)装饰器模式虽然从数量级上减少了类的数量,但是为了要装饰,仍旧会增加很多的小类这些具体的装饰类的逻辑将不会非常的清晰,不够直观,容易令人迷惑。

(3)多层的装饰是比较复杂的。为什么会复杂?你想想看,就像剥洋葱一样,你剥到最后才发现是最里层的装饰出现了问题,可以想象一下工作量。这点从我使用Java I/O的类库就深有感受,我只需要单一结果的流,结果却往往需要创建多个对象,一层套一层,对于初学者来说容易让人迷惑。

四、装饰器模式的使用场景

1、当你想要给一个类增加功能,然而,却并不想修改原来类的代码时,可以考虑装饰器模式如果你想要动态的给一个类增加功能,并且这个功能你还希望可以动态的撤销,就好像直接拿掉了一层装饰物;

2、比如java里面的基本数据类型int、boolean、char....都有它们对应的装饰类Integer、Boolean、Character....

3、在Java IO中,具体构建角色是节点流、装饰角色是过滤流;

FilterInputStream和FilterOutputStream是装饰角色,而其他派生自它们的类则是具体装饰角色。

DataoutputStream out=new DataoutputStream(new FileoutputStream());

这就是 装饰者模式,DataoutputStream是装饰者子类,FileoutputStream是实现接口的子类。

这里不会调用到装饰者类--FilteroutputStream,只是作为继承的另一种方案,对客户端来说是透明的,是为了功能的扩张。

五、装饰器模式实现手抓饼

老板,来个手抓饼,加个蛋、加根烤肠多少钱?

这个就是装饰器模式,用蛋和烤肠去装饰手抓饼,让手抓饼更加美味。

1、Component,抽象构件:街边小吃

package designMode.advance.decorator;public abstract class Snack {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();
}

2、ConcreteComponent,具体构件,或者基础构件

(1)手抓饼

package designMode.advance.decorator;public class HandGrabCake extends Snack {public HandGrabCake() {setPrice(5.0f);setDes(" 手抓饼 "+cost());}@Overridepublic float cost() {return super.getPrice();}
}

(2)烤冷面

package designMode.advance.decorator;public class GrilledColdNoodles extends Snack {public GrilledColdNoodles() {setPrice(4.0f);setDes(" 烤冷面 "+cost());}@Overridepublic float cost() {return super.getPrice();}
}

Decorator,装饰角色

package designMode.advance.decorator;public class Decorator extends Snack {private Snack obj;public Decorator(Snack obj) { //组合this.obj = obj;}@Overridepublic float cost() {return super.getPrice() + obj.cost();}@Overridepublic String getDes() {// obj.getDes() 输出被装饰者的信息return des + " " + getPrice() + " && " + obj.getDes();}
}

3、具体装饰角色

(1)鸡蛋

package designMode.advance.decorator;public class Egg extends Decorator {public Egg(Snack obj) {super(obj);setDes(" 鸡蛋 ");setPrice(1.0f);}
}

(2)烤肠

package designMode.advance.decorator;public class Sausage extends Decorator  {public Sausage(Snack obj) {super(obj);setDes(" 烤肠 ");setPrice(2.0f);}
}

(3)金针菇

package designMode.advance.decorator;public class NeedleMushroom extends Decorator{public NeedleMushroom(Snack obj) {super(obj);setDes(" 金针菇 ");setPrice(2.5f);}
}

4、老板,来个手抓饼,加2个蛋、加1根烤肠

package designMode.advance.decorator;public class HandGrabCakeBar {public static void main(String[] args) {// 装饰者模式下的订单:2个蛋+一根烤肠的手抓饼// 1. 点一份手抓饼Snack order = new HandGrabCake();System.out.println("小白手抓饼费用=" + order.cost());System.out.println("描述=" + order.getDes());// 2. order 加入一个鸡蛋order = new Egg(order);System.out.println("手抓饼 加入1个鸡蛋 费用 =" + order.cost());System.out.println("手抓饼 加入1个鸡蛋 描述 = " + order.getDes());// 3. order 加入一个鸡蛋order = new Egg(order);System.out.println("手抓饼 加入1个鸡蛋 加入2个鸡蛋 费用 =" + order.cost());System.out.println("手抓饼 加入1个鸡蛋 加入2个鸡蛋 描述 = " + order.getDes());// 3. order 加入一根烤肠order = new Sausage(order);System.out.println("手抓饼 加入1个鸡蛋 加入2个鸡蛋 加1根烤肠 费用 =" + order.cost());System.out.println("手抓饼 加入1个鸡蛋 加入2个鸡蛋 加1根烤肠 描述 = " + order.getDes());System.out.println("===========================");Snack order2 = new GrilledColdNoodles();System.out.println("考冷面 费用 =" + order2.cost());System.out.println("考冷面 描述 = " + order2.getDes());// 1. order2 加入一袋金针菇order2 = new NeedleMushroom(order2);System.out.println("考冷面 加入一袋金针菇  费用 =" + order2.cost());System.out.println("考冷面 加入一袋金针菇 描述 = " + order2.getDes());}
}

5、好嘞,您拿好

六、装饰器模式在Java I/O系统中的实现

前面总结了这么多,再从大神们的作品中找一个实际应用例子吧,毕竟那是经历实战检验的,肯定是有道理的。嗯,在平时的留意中我发现Java I/O系统的设计中用到了这一设计模式,因为Java I/O类库需要多种不同功能的组合。这里我就以InputStream为例简单说明一下,同样我们还是来看一下其类图:

InputStream作为抽象构件,其下面大约有如下几种具体基础构件,从不同的数据源产生输入:

  • ByteArrayInputStream,从字节数组产生输入;
  • FileInputStream,从文件产生输入;
  • StringBufferInputStream,从String对象产生输入;
  • PipedInputStream,从管道产生输入;
  • SequenceInputStream,可将其他流收集合并到一个流内;

FilterInputStream作为装饰器在JDK中是一个普通类,其下面有多个具体装饰器比如BufferedInputStream、DataInputStream等。我们以BufferedInputStream为例,使用它就是避免每次读取时都进行实际的写操作,起着缓冲作用。我们可以在这里稍微深入一下,站在源码的角度来管中窥豹。

FilterInputStream内部封装了基础构件:

protected volatile InputStream in;

而BufferedInputStream在调用其read()读取数据时会委托基础构件来进行更底层的操作,而它自己所起的装饰作用就是缓冲,在源码中可以很清楚的看到这一切:

public synchronized int read() throws IOException {if (pos >= count) {fill();if (pos >= count)return -1;}return getBufIfOpen()[pos++] & 0xff;
}private void fill() throws IOException {byte[] buffer = getBufIfOpen();if (markpos < 0)pos = 0;            /* no mark: throw away the buffer */else if (pos >= buffer.length)  /* no room left in buffer */if (markpos > 0) {  /* can throw away early part of the buffer */int sz = pos - markpos;System.arraycopy(buffer, markpos, buffer, 0, sz);pos = sz;markpos = 0;} else if (buffer.length >= marklimit) {markpos = -1;   /* buffer got too big, invalidate mark */pos = 0;        /* drop buffer contents */} else if (buffer.length >= MAX_BUFFER_SIZE) {throw new OutOfMemoryError("Required array size too large");} else {            /* grow buffer */int nsz = (pos <= MAX_BUFFER_SIZE - pos) ?pos * 2 : MAX_BUFFER_SIZE;if (nsz > marklimit)nsz = marklimit;byte nbuf[] = new byte[nsz];System.arraycopy(buffer, 0, nbuf, 0, pos);if (!bufUpdater.compareAndSet(this, buffer, nbuf)) {throw new IOException("Stream closed");}buffer = nbuf;}count = pos;// 看这行就行了,委托基础构件来进行更底层的操作int n = getInIfOpen().read(buffer, pos, buffer.length - pos);if (n > 0)count = n + pos;
}private InputStream getInIfOpen() throws IOException {InputStream input = in;if (input == null)throw new IOException("Stream closed");return input;
}

这部分的代码很多,这里我们没有必要考虑这段代码的具体逻辑,只需要看到在BufferedInputStream的read方法中通过getInIfOpen()获取基础构件从而委托其进行更底层的操作(在这里是读取单个字节)就可以说明本文所要说的一切了。

至于I/O类库中的其他设计诸如OutputStream、Writer、Reader,是一致的,这里就不再赘述了。

【源码分析设计模式 5】Java I/O系统中的装饰器模式相关推荐

  1. 【附源码】计算机毕业设计JAVA智能导诊系统

    [附源码]计算机毕业设计JAVA智能导诊系统 目运行 环境项配置: Jdk1.8 + Tomcat8.5 + Mysql + HBuilderX(Webstorm也行)+ Eclispe(Intell ...

  2. 【附源码】计算机毕业设计JAVA智慧后勤系统

    [附源码]计算机毕业设计JAVA智慧后勤系统 目运行 环境项配置: Jdk1.8 + Tomcat8.5 + Mysql + HBuilderX(Webstorm也行)+ Eclispe(Intell ...

  3. openxr runtime Monado 源码解析 源码分析:CreateInstance流程(设备系统和合成器系统)Compositor comp_main client compositor

    monado系列文章索引汇总: openxr runtime Monado 源码解析 源码分析:源码编译 准备工作说明 hello_xr解读 openxr runtime Monado 源码解析 源码 ...

  4. 【附源码】计算机毕业设计JAVA影片租赁系统

    [附源码]计算机毕业设计JAVA影片租赁系统 目运行 环境项配置: Jdk1.8 + Tomcat8.5 + Mysql + HBuilderX(Webstorm也行)+ Eclispe(Intell ...

  5. 【SA8295P 源码分析】44 - 如何替换 NON-HLOS.bin 中的 Wifi Firmware 固件

    [SA8295P 源码分析]44 - 如何替换 NON-HLOS.bin 中的 Wifi Firmware 固件 1.提取 NON-HLOS.bin 中的 Wifi Firmware 出来 2.把提取 ...

  6. 若依管理系统源码分析-导出Excel时怎样通过注解中readConverterExp实现格式化导出列的显示格式(0和1格式化为是否)

    场景 在数据库中存储某些个是否的字段时,有时会使用tinint进行存储 这样会在实体类中生成布尔类型的变量 private Boolean sfkt; 有时也会用int进行存储是否 那么在实体类中就可 ...

  7. 【转】ABP源码分析四十六:ABP ZERO中的Ldap模块

    通过AD作为用户认证的数据源.整个管理用户认证逻辑就在LdapAuthenticationSource类中实现. LdapSettingProvider:定义LDAP的setting和提供Defaut ...

  8. 【转】ABP源码分析四十五:ABP ZERO中的EntityFramework模块

    AbpZeroDbContext:配置ABP.Zero中定义的entity的Dbset EntityFrameworkModelBuilderExtensions:给PrimitiveProperty ...

  9. 【转】ABP源码分析二十六:核心框架中的一些其他功能

    本文是ABP核心项目源码分析的最后一篇,介绍一些前面遗漏的功能 AbpSession AbpSession: 目前这个和CLR的Session没有什么直接的联系.当然可以自定义的去实现IAbpSess ...

最新文章

  1. springboot-springmvc-requestParam
  2. 三个线程按循序一个打印A一个打印B一个打印C 循环打印?
  3. HTML DOM appendChild() 方法
  4. Amazon Aurora Serverless MySQL已正式可用
  5. linux中awk工具的使用(转载)
  6. 为什么php md5,为什么php md5()总是与python的不同哈希.md5()如果使用汉字?
  7. 如何在Android上显示警报对话框?
  8. iOS label显示不同颜色的字体
  9. 20191230每日一句
  10. python分组统计excel数据_python中excel数据分组处理
  11. 知识管理在企业竞争发展中的作用
  12. photoshop调人像冷色
  13. 阿里云国际站:阿里云服务器遇到了CC攻击怎么处理防护措施?
  14. 才刚满30岁,就中年危机了...
  15. 阴阳师辅助(基于按键精灵)
  16. scrum立会报告+燃尽图(第三周第三次)
  17. 交易思想之顺大势逆小势
  18. http.Request
  19. 怎么让在线视频播放html,HTML5网页视频强制变速倍速播放
  20. 苹果六电池_苹果官网上架新品,18999元起~

热门文章

  1. Pycharm使用anaconda环境 (原环境 base)
  2. 关于org.apache.poi.ss.usermodel 操作excel数据丢失问题
  3. Windows下的Telnet
  4. prestashop 菜单和快速通道翻译
  5. java服务架构 之MGW(美团点评高性能四层负载均衡)
  6. 瀚高股份吕新杰:创新开源双驱动 躬耕国产数据库
  7. Linux电源管理之Runtime PM
  8. 【安卓SDK学习之anyChatSDK】 1_1 实现用户登录和房间进出(根据官方HelloAnyChat的demo整理的简要的开发流程)
  9. python编程单片机_Python与C51单片机交互
  10. 联想E440设置U盘启动的方法