定义

  在不改变原有对象的基础之上,将功能附加到对象上

适用场景

  1. 扩展一个类的功能
  2. 动态的给对象增加功能,当功能不需要的时候能够动态删除

详解

  在看到定义的时候,可能很多人会想,这不就是继承吗?的确很像,不过是比继承更加有弹性的替代方案。就像原型模式和new之间的关系一样,有区别,但是区别又不是特别大。装饰者一个很重要的词就是动态,他可以灵活的选择要这个功能还是不要。在装饰者中要有四个角色:抽象的实体类,具体的实体类,抽象的装饰者,具体的装饰者。下面画一个大致的UML图

  实体类创建之后,如果想扩展实体类那么一般的想法是继承,当然继承也是其中方法之一。上面由UML图就可以看出,在装饰者模式下,我们可以通过装饰类下的扩展功能实现对抽象实体类的包装。当我们需要扩展功能就多加一个扩展功能,不需要扩展功能就不用添加,是一个很动态的扩展方式。下面具体看代码解析。

抽象实体类

public abstract class abstractCake {
protected abstract String getCake();
protected abstract int cost(); }

实体类

public class cake extends abstractCake{
@Override
protected String getCake() {
return "蛋糕";
} @Override
protected int cost() {
return 100;
}
}

抽象装饰类(可以抽象也可以不抽象)

public class AbstractDecorator extends abstractCake {
private abstractCake abstractcake; public AbstractDecorator(abstractCake abstractcake) {
this.abstractcake = abstractcake;
} @Override
protected String getCake() {
return this.abstractcake.getCake();
} @Override
protected int cost() {
return this.abstractcake.cost();
}
}

扩展功能1

public class FruitDecorator extends AbstractDecorator {
public FruitDecorator(abstractCake abstractcake) {
super(abstractcake);
} @Override
protected String getCake() {
return super.getCake()+" 加一层水果";
} @Override
protected int cost() {
return super.cost()+20;
}
}

扩展功能2

public class SugerDecorator extends AbstractDecorator{
public SugerDecorator(abstractCake abstractcake) {
super(abstractcake);
} @Override
protected String getCake() {
return super.getCake()+" 多加糖";
} @Override
protected int cost() {
return super.cost()+10;
}
}

测试类

public class Test {
public static void main(String[] args) { abstractCake ac=new cake();
ac=new FruitDecorator(ac);
ac=new SugerDecorator(ac);
System.out.println(ac.getCake()); }
}

结果

具体的内存图

  可以很清楚的看见ac这个抽象类下面一层层包着实体类,解析的时候每一层的包装类的getCake()方法都会送回给抽象实体类,而我的getCake()方法实现的是字符串增加。后面如果我还需要一层水果,那么我就多装饰一次。如果用继承实现,那么我要两层水果,不要糖,那么我就还需要再定义一个实现两层水果的子类,而装饰者模式就只需要修改客户端的ac类实现就行。在JDK中装饰者模式用的最出名的莫过于IO流了,以InputStream为例子。下面是UML图

  这就是典型的装饰者。平常我们只需要FileInputStream来读取文件,当我们有一些特殊功能需要,如:BufferedInputStream(完成了缓存功能,使读取文件速度大大提升)和DataInputStream(把byte转换成Java基本数据类型)我们就可以装饰一下InputStream,即灵活又方便。这样使得我们对FileInputStream流的处理更能达到我们想要的预期。当你理解了装饰者模式,我推荐你看这篇博客。这样你就能很好的理解IO流的操作。

总结

  装饰者模式是一个比继承更加灵活的扩展方式,要记住装饰者模式必不可少的4个类:抽象的实体类,具体的实体类,抽象的装饰者,具体的装饰者。再结合上面推荐的博客,我相信你不但能理解装饰者,还能更加清晰的理解JDK中的IO流

结合JDK源码看设计模式——装饰者模式的更多相关文章

  1. 结合JDK源码看设计模式——享元模式

    前言 在说享元模式之前,你一定见到过这样的面试题 public class Test { public static void main(String[] args) { Integer a=Inte ...

  2. 结合JDK源码看设计模式——桥接模式

    前言: 在我们还没学习框架之前,肯定都学过JDBC.百度百科对JDBC是这样介绍的[JDBC(Java DataBase Connectivity,java数据库连接)是一种用于执行SQL语句的Jav ...

  3. 结合JDK源码看设计模式——单例模式

    定义: 保证一个类仅有一个实例,并提供一个全局访问点 适用场景: 确保任何情况下这个对象只有一个实例 详解: 私有构造器 单利模式中的线程安全+延时加载 序列化和反序列化安全, 防止反射攻击 结合JD ...

  4. 结合JDK源码看设计模式——原型模式

    定义: 指原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象.不需要知道任何创建的细节,不调用构造函数适用场景: 类初始化的时候消耗较多资源 new产生的对象需要非常繁琐的过程 构造函数比较 ...

  5. 结合JDK源码看设计模式——简单工厂、工厂方法、抽象工厂

    三种工厂模式的详解: 简单工厂模式: 适用场景:工厂类负责创建的对象较少,客户端只关心传入工厂类的参数,对于如何创建对象的逻辑不关心 缺点:如果要新加产品,就需要修改工厂类的判断逻辑,违背软件设计中的 ...

  6. 结合JDK源码看设计模式——适配器模式

    定义: 将一个类的接口转换成客户期望的另外一个接口(重点理解适配的这两个字),使得接口不兼容的类可以一起工作适用场景: 已经存在的类,它的方法和需求不匹配的时候 在软件维护阶段考虑的设计模式 详解 首 ...

  7. 结合JDK源码看设计模式——模板方法模式

    前言: 相信很多人都听过一个问题:把大象关进冰箱门,需要几步? 第一,把冰箱门打开:第二,把大象放进去:第三,把冰箱门关上.我们可以看见,这个问题的答案回答的很有步骤.接下来我们介绍一种设计模式--模 ...

  8. 结合JDK源码看设计模式——迭代器模式

    前言: Iterator翻译过来就是迭代器的意思.在前面的工厂模式中就介绍过了iterator,不过当时介绍的是方法,现在从Iterator接口的设计来看,似乎又是一种设计模式,下面我们就来讲讲迭代器 ...

  9. 结合JDK源码看设计模式——建造者模式

    概念: 将一个复杂对象的构建与它的表示分离.使得同样构建过程可以创建不同表示适用场景: 一个对象有很多属性的情况下 想把复杂的对象创建和使用分离 优点: 封装性好,扩展性好 详解: 工厂模式注重把这个 ...

随机推荐

  1. 第七章——集成学习和随机森林(Ensemble Learning and Random Forests)

    俗话说,三个臭皮匠顶个诸葛亮.类似的,如果集成一系列分类器的预测结果,也将会得到由于单个预测期的预测结果.一组预测期称为一个集合(ensemble),因此这一技术被称为集成学习(Ensemble Le ...

  2. eclipse工具的使用心得

    一.eclipse工具的使用 eclipse是一个开源的IDE,进行javaEE开发一般使用myeclipse插件比较方便 1. java代码的位置 1)选择工作空间workspace 选择一个文件夹 ...

  3. 玩转spring MVC(九)---Spring Data JPA

    偷个懒 在网上看有写的比较好的,直接贴个链接吧:http://***/forum/blogPost/list/7000.html 版权声明:本文为博主原创文章,未经博主允许不得转载.

  4. CSS布局-圣杯布局

    圣杯布局 圣杯布局很完美(兼容所有浏览器,包括IE6),但是使用了相对定位,布局有局限性,宽度控制要改的地方也多. 第一种方法公用部分: .lgyz, .lzrg, .lrzcg, .lcgrz, . ...

  5. yum安装指定版本的软件包的方法

    yum默认都是安装最新版的软件,这样可能会出一些问题,或者我们希望yum安装指定(特定)版本(旧版本)软件包.所以,就顺带分享yum安装指定(特定)版本(旧版本)软件包的方法. 过程如下:假设这里是我 ...

  6. Spring Cloud 多版本管理那些事。

    好久没有研究 Spring Cloud 了,也没有关注它的更新及新特性,上官网看了下,又增加了几个版本,有正式版有预览版,多达 6 个版本,实在让人蒙逼. 而我们的项目版本还仪停留在 Dalston ...

  7. types.go

    } type ChannelStatsList []*ChannelStats func (c ChannelStatsList) Len() int { return len(c) } func ( ...

  8. ConnectionString 属性尚未初始化

    关于"ConnectionString 属性尚未初始化"的问题(如下图),      我在下面一段代码中发现了问题所在:   public bool ReturnFlag(stri ...

  9. solr+jieba结巴分词

    为什么选择结巴分词 分词效率高 词料库构建时使用的是jieba (python) 结巴分词Java版本 下载 git clone https://github.com/huaban/jieba-ana ...

  10. 前端基础-CSS的各种选择器的特点以及CSS的三大特性

    一. 基本选择器 二. 后代选择器.子元素选择器 三. 兄弟选择器 四. 交集选择器与并集选择器 五. 序列选择器 六. 属性选择器 七. 伪类选择器 八. 伪元素选择器 九. CSS三大特性 一. ...