装饰者模式(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 ...
随机推荐
- (二)mybatis框架原理(图解)
目录 mybatis 框架原理图(粗略版) mybatis 框架原理图(粗略版)
- Redis 和 Memcached 各有什么优缺点,主要的应用场景是什么样的?
1.显示最新的项目列表 2.删除与过滤 3.排行榜相关 4.按照用户投票和时间排序 5.处理过期项目 6.计数 7.特定时间内的特定项目 8.实时分析正在发生的情况,用于数据统计与防止垃圾邮件等 9. ...
- python学习-3 python基础-1基础知识和解释器
1.基础知识 ~后缀名是可以是任意的 ~导入模块时,如果不是.py就会报错 =>>所以尽量后缀名携程.py 2.执行方式 -python解释器 3. #!/usr/bin/env py ...
- Python【条件判断】
单向判断ifif xxx: #冒号 #条件 print(xxx) #缩进是四个空格或一个Tab键#被缩进的内容(print()函数)和if条件语句组成了一个代码块(一个整体)————————————— ...
- sql 行数据找出最大的及所有数据最大的
SELECT @charges=ISNULL(MAX(a.maxcharge), 0.00) FROM( SELECT (SELECT MAX(maxcharge) FROM(VALUES(ilong ...
- 【opencv 源码剖析】 四、 Mat的赋值构造函数 和 拷贝构造函数
1.赋值构造函数 右值引用 inline Mat& Mat::operator = (Mat&& m) { if (this == &m) return *this; ...
- 轮播图--使用原生js的轮播图
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- linux Linux入门
Linux入门 Linux学习什么? 常用命令(背会) 软件安装(熟练) 服务端的架构(开开眼界) Linux如何学习? 不要问那么多为什么,后面你就懒得问了 先尝试理解一下,不行就背下来 一个知识点 ...
- ECharts简单入门demo
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...
- About Spring MVC
一.简介 1.Springmvc是什么 Spring Web MVC是一种基于Java的实现了Web MVC设计模式的请求驱动类型的轻量级Web框架,即使用了MVC架构模式的思想,将web层进行职责解 ...