装饰者模式(Decorator)---结构型
1 基础知识
定义:在不改变原有对象的基础上,将功能附加到对象上即动态地给一个对象添加一些额外的职责。特征:提供了比继承更有弹性的替代方案。
本质:动态组合。
使用场景:扩展一个类的功能或给一个类添加附加的职责;动态给一个对象添加功能,这些功能还可以动态撤销。
优点:比继承更加灵活,继承是静态的在设计之初就确定好了而装饰者是动态的扩展功能;通过使用不同的装饰类或装饰类的排列组合可以达到不同的效果;符合开闭原则。缺点:会出现更多的代码,更多的类使程序复杂;动态装饰时,多层装饰会更复杂。
2 代码示例
场景:设若有一个计算奖金的对象,现在需要能够灵活地给它增加和减少功能,还需要能够动态地组合功能,每个功能就相当于在计算奖金的某个部分。现在的问题就是,如何才能够透明地给一个对象增加功能,并实现功能的动态组合?在此种情况下可以用到装饰者模式,所谓透明增加功能就是给一个对象增加功能但是不能让对象知道。在使用时可以考虑一个更为简单的场景:假设商家做了许多煎饼,这些煎饼有的加蛋,有的加烤肠那么如何计算出这些不同种类的的煎饼?
煎饼类:Battercake
public class Battercake {
//描述
protected String getDesc(){
return "煎饼";
}
//煎饼的基本价格
protected int cost(){
return 8;
}
}
煎饼加鸡蛋类:BattercakeWithEgg 继承煎饼类
public class BattercakeWithEgg extends Battercake {
@Override
public String getDesc() {
return super.getDesc()+" 加一个鸡蛋";
}
@Override
//重写父类花费方法
public int cost() {
return super.cost()+1;
}
}
煎饼加鸡蛋类加香肠类:BattercakeWithEggSausage 继承煎饼加鸡蛋类
public class BattercakeWithEggSausage extends BattercakeWithEgg {
@Override
public String getDesc() {
return super.getDesc()+ " 加一根香肠";
}
@Override
public int cost() {
return super.cost()+2;
}
}
应用层:Test
public class Test {
public static void main(String[] args) {
//基本的煎饼
Battercake battercake = new Battercake();
System.out.println(battercake.getDesc()+" 销售价格:"+battercake.cost());
//加鸡蛋的类
Battercake battercakeWithEgg = new BattercakeWithEgg();
System.out.println(battercakeWithEgg.getDesc()+" 销售价格:"+battercakeWithEgg.cost());
//加鸡蛋加香肠的类
Battercake battercakeWithEggSausage = new BattercakeWithEggSausage();
System.out.println(battercakeWithEggSausage.getDesc()+" 销售价格:"+battercakeWithEggSausage.cost());
}
}
此时如果想加两个鸡蛋,加三个香肠等则需要分别建立对应的类这样可以预见一定会随着的需求的复杂而使得的类的数目上升,造成类爆炸。那么对于这种情形的解决方案便是装饰者模式。在装饰者模式中要有抽象的实体类、具体的实体类,抽象的装饰者、具体的装饰者根据这些来编写代码。
抽象的实体类:ABattercake
public abstract class ABattercake {
protected abstract String getDesc();
protected abstract int cost();
}
具体的实体类煎饼类:Battercake 继承抽象实体类
public class Battercake extends ABattercake {
@Override
protected String getDesc() {
return "煎饼";
}
@Override
protected int cost() {
return 8;
}
}
抽象的装饰类:AbstractDecorator
//抽象的装饰者继承了抽象的实体
public abstract class AbstractDecorator extends Battercake {
private ABattercake aBattercake;
//通过构造器注入对象
public AbstractDecorator(ABattercake aBattercake) {
this.aBattercake = aBattercake;
} protected abstract void doSomething(); @Override
protected String getDesc() {
return this.aBattercake.getDesc();
} @Override
protected int cost() {
return this.aBattercake.cost();
}
}
具体的装饰类香肠类:SausageDecorator 继承抽象装饰者
public class SausageDecorator extends AbstractDecorator{
//因为父类中没有无参构造所以要在子类中使用有参构造调用父类的的有参构造
// 因此要在子类中实现默认的构造器
public SausageDecorator(ABattercake aBattercake) {
super(aBattercake);
}
@Override
protected void doSomething() {
}
@Override
protected String getDesc() {
return super.getDesc()+" 加一根香肠";
}
@Override
protected int cost() {
return super.cost()+2;
}
}
具体的装饰者类鸡蛋类:EggDecorator
public class EggDecorator extends AbstractDecorator {
public EggDecorator(ABattercake aBattercake) {
super(aBattercake);
}
@Override
protected void doSomething() {
}
@Override
protected String getDesc() {
return super.getDesc()+" 加一个鸡蛋";
}
@Override
protected int cost() {
return super.cost()+1;
}
}
应用层:
public class Test {
public static void main(String[] args) {
ABattercake aBattercake = new Battercake();
//加两个鸡蛋
aBattercake = new EggDecorator(aBattercake);
aBattercake = new EggDecorator(aBattercake);
//加一个香肠
aBattercake = new SausageDecorator(aBattercake);
System.out.println(aBattercake.getDesc()+" 销售价格:"+aBattercake.cost());
}
}
类关系图:

注:
在抽象的装饰类中有一个dosomething方法,这个方法在这个业务逻辑中只是为了让装饰类是抽象的而已并无任何业务逻辑,从这里也可以看出抽象的装饰类在不同的业务情况下也可以不是抽象的。
3 源代码中的使用
(1)JDK中
IO包下的BufferedReader类:
public class BufferedReader extends Reader {
private Reader in;
public BufferedReader(Reader in, int sz) {
super(in);
if (sz <= 0)
throw new IllegalArgumentException("Buffer size <= 0");
this.in = in
cb = new char[sz];
}
Reader是一个抽象的类,就相当于抽象的煎饼类,BufferedReader上面中的抽象的装饰者类,通过构造器注入Reader对象,在这里承担抽象装饰者类的角色但不是抽象类。
在IO流中对于输入流其类图关系如下:

InputStream就是抽象的装饰者类,FilterInputStream是装饰者,但因为装饰者比较多因此做了一层封装,下面的LineInputStream、BufferdInputStream和DataInputStream是具体的装饰者。与此类似的还有输出流中的类OutputStream。
4 相关模式
(1)装饰模式与适配器模式
这是两个没有什么关联的模式,放到一起来说,是因为它们有一个共同的别名:Wrapper这两个模式功能上是不一样的,适配器模式是用来改变接口的,而装饰模式是用来改变对象功能的。
(2)装饰模式与组合模式
这两个模式有相似之处,都涉及到对象的递归调用,从某个角度来说,可以把装饰看做是只有一个组件的组合。但是它们的目的完全不一样,装饰模式是要动态地给对象增加功能;而组合模式是想要管理组合对象和叶子对象,为它们提供一个一致的操作接口给客户端,方便客户端的使用。
(3)装饰模式与策略模式
这两个模式可以组合使用。策略模式也可以实现动态地改变对象的功能,但是策略模式只是一层选择,也就是根据策略选择一下具体的实现类而已。而装饰模式不是一层,而是递归调用,无数层都可以,只要组合好装饰器的对象组合,那就可以依次调用下去。所以装饰模式更灵活。而且策略模式改变的是原始对象的功能,不像装饰模式,后面一个装饰器,改变的是经过前一个装饰器装饰后的对象。也就是策略模式改变的是对象的内核,而装饰模式改变的是对象的外壳。
这两个模式可以组合使用,可以在一个具体的装饰器中使用策略模式来选择更具体的实现方式。比如前面计算奖金的另外一个问题就是参与计算的基数不同,奖金的计算方式也是不同的。举例来说:假设张三和李四参与同一个奖金的计算,张三的销售总额是2万元,而李四的销售总额是8万元,它们的计算公式是不一样的,假设奖金的计算规则是,销售额在5万以下,统一3%,而5万以上,5万内是4%,超过部分是6%参与同一个奖金的计算,这就意味着可以使用同一个装饰器,但是在装饰器的内部,不同条件下计算公式不一样,那么怎么选择具体的实现策略呢?自然使用策略模式就可以了,也就是装饰模式和策略模式组合来使用。
(4)装饰模式与模板方法模式
这是两个功能上有相似点的模式。模板方法模式主要应用在算法骨架固定的情况,那么要是算法步骤不固定呢,也就是一个相对动态的算法步骤,就可以使用装饰模式了,因为在使用装饰模式的时候,进行装饰器的组装,其实也相当于是一个调用算法步骤的组装,相当于是一个动态的算法骨架。既然装饰模式可以实现动态的算法步骤的组装和调用,那么把这些算法步骤固定下来,那就是模板方法模式实现的功能了,因此装饰模式可以模拟实现模板方法模式的功能。
0
装饰者模式(Decorator)---结构型的更多相关文章
- 装饰器模式 Decorator 结构型 设计模式 (十)
引子 现实世界的装饰器模式 大家应该都吃过手抓饼,本文装饰器模式以手抓饼为模型展开简介 "老板,来一个手抓饼, 加个培根, 加个鸡蛋,多少钱?" 这句话会不 ...
- Java设计模式07:常用设计模式之装饰器模式(结构型模式)
1. Java之装饰器模式(Decorator Pattern) (1)概述: 装饰模式在Java种使用也很广泛,比如我们在重新定义按钮.对话框等时候,实际上已经在使用装饰模式了.在不必改变原 ...
- 设计模式(八)装饰器模式Decorator(结构型)
设计模式(八)装饰器模式Decorator(结构型) 1. 概述 若你从事过面向对象开发,实现给一个类或对象增加行为,使用继承机制,这是所有面向对象语言的一个基本特性.如果已经存在的一个类缺少某些方法 ...
- 修饰模式(Decorator结构型)C#简单例子
修饰模式(Decorator结构型)C#简单例子 玩家基本功能是移动.运行等等.BaseAbility新增加功能:1.伤害技能harmAbility:2.阻碍技能BaulkAbility:3.辅助技能 ...
- 桥接模式 桥梁模式 bridge 结构型 设计模式(十二)
桥接模式Bridge Bridge 意为桥梁,桥接模式的作用就像桥梁一样,用于把两件事物连接起来 意图 将抽象部分与他的实现部分进行分离,使得他们都可以独立的发展. 意图解析 依赖倒置原 ...
- 设计模式10: Facade 外观模式(结构型模式)
Facade 外观模式(结构型模式) 系统的复杂度 假设我们要开发一个坦克模式系统用于模拟坦克车在各种作战环境中的行为,其中坦克系统由引擎.控制器.车轮.车身等各个子系统构成. internal cl ...
- 组合模式/composite模式/对象结构型模式
组合模式/composite模式/对象结构型 意图 将对象组合成树形结构以表示"整体--部分"的层次结构.Composite使得用户对单个对象和组合对象的使用具有一致性. 动机 C ...
- 浅谈设计模式--装饰者模式(Decorator Pattern)
挖了设计模式这个坑,得继续填上.继续设计模式之路.这次讨论的模式,是 装饰者模式(Decorator Pattern) 装饰者模式,有时也叫包装者(Wrapper),主要用于静态或动态地为一个特定的对 ...
- 【PHP设计模式 09_ZhuangShiQi.php】装饰器模式 (decorator)
<?php /** * [装饰器模式 (decorator)] * 有时候发布一篇文章需要经过很多人手,层层处理 */ header("Content-type: text/html; ...
- 设计模式 - 装饰者模式(Decorator Pattern) Java的IO类 用法
装饰者模式(Decorator Pattern) Java的IO类 用法 本文地址: http://blog.csdn.net/caroline_wendy/article/details/26716 ...
随机推荐
- 【51nod】2027 期望问题
[51nod]2027 期望问题 %%%zsy 看不懂题解的垃圾选手在zsy大佬的讲解下终于知道了这道题咋做-- 先把所有\(a\)从大到小排序 设\(f_{i}\)为前\(i\)个数组成的排列的值, ...
- vue的基本语法
在学习vue之前,我们应了解一下什么是vue.js? 什么是Vue.js? Vue.js是目前最后一个前端框架,React是最流行的一个前端框架(react除了开发网站,还可以开发手机App,Vue语 ...
- Ubuntu使用Shadow socks-qt5
由于大多数朋友都问我在Ubuntu上面怎么kexueshangwang,为了防止以后忘记,故此记录. 本教程使用的配置 Ubuntu 16.10Shadowsocks-qt5一个可用的ss账号一根能够 ...
- idea 编辑器Git暂存区的使用
平时在开发时候 一般线上环境和线下环境区别会很大,所以一下线下的自己测试环境的代码没有如果提交会影响线上环境,所以一般都会使用git的一个暂存区作为临时存放不需要提交的代码,这样每次提交代码都可以在不 ...
- 解决python在cmd运行时导入包失败,出现错误信息 "ModuleNotFoundError: No module named ***"
1.下图为我的自动化测试工程结构图 我通过运行run.bat批处理文件,调用cmd控制台运行start_run.py来开始我的自动化测试,但是出现如下错误: 大家可能知道我们的工程在IDE(Pycha ...
- DataTime.Now.Ticks
getTime public long getTime() 返回自 1970 年 1 月 1 日 00:00:00 GMT 以来此 Date 对象表示的毫秒数. 返回: 自 1970 年 1 月 1 ...
- Java后端开发常用工具
Java后端开发常用工具推荐: 俗话说,工欲善其事,必先利其器.不过初学时候不大建议过度依赖IDE等过多工具,这会让自己的编程基础功变得很差,比如各种语法的不熟悉,各种关键字比如synchronize ...
- ssh-copy-id三步实现SSH无密码登录和ssh常用命令
第一步:在本地机器上使用ssh-keygen产生公钥私钥对 $ ssh-keygen 第二步:用ssh-copy-id将公钥复制到远程机器中 $ ssh-copy-id -i .ssh/id_rsa ...
- CHD-5.3.6集群安装
我是基于Apache-hadoop2.7.3版本安装成功后,已有的环境进行安装chd-5..6 已用的环境: JDK版本: java version "1.8.0_191" Jav ...
- 30K以上的高薪Java程序员所需技能大汇总
总所周知,Java是目前使用最为广泛的网络编程语言之一. 它具有简单,面向对象,稳定,与平台无关,解释型,多线程,动态等特点. 一般的JAVA程序员或许只需知道一些JAVA的语法结构就可以应付了.但要 ...