装饰器模式 Decorator 结构型 设计模式 (十)
引子


现实世界的装饰器模式
"老板,来一个手抓饼, 加个培根, 加个鸡蛋,多少钱?" |
1.手抓饼 核心角色 |
2.配菜(鸡蛋/培根/香肠...) 装饰器角色 |
那么我们有多少种手抓饼呢?
原味手抓饼/加鸡蛋手抓饼/加鸡蛋加培根手抓饼/加鸡蛋加烤肠手抓饼/加鸡蛋加培根加烤肠手抓饼手抓饼/.......
很显然,这就是数学中的组合,最终的个数跟我们到底有多少种配菜有关系
|
在现实世界里面,你会很自然的说 "老板,来一个手抓饼, 加个培根, 加个鸡蛋,多少钱?""
|
那么为什么在程序世界里面,你却很可能说"老板,给我来一个加了鸡蛋加了培根的那种手抓饼" 呢? |
手抓饼代码示例
手抓饼接口和具体的一家店铺提供的手抓饼
package decorator;
/**
* Created by noteless on 2018/9/6.
* Description:手抓饼接口 描述抽象的手抓饼
*/
public interface HandPancake {
/**
* 提供手抓饼
*/
String offerHandPancake();
/**计算手抓饼的价格
* @return
*/
Integer calcCost();
} package decorator;
/**
* Created by noteless on 2018/9/6.
* Description: Noteless 家的手抓饼
*/
public class NotelessHandPancake implements HandPancake {
/**
* 提供noteless 家的手抓饼一份
*/
@Override
public String offerHandPancake() {
return " noteless 家的手抓饼";
}
/**计算 noteless 家 一份手抓饼的价格
* @return
*/
@Override
public Integer calcCost() {
return 3;
}
}
配菜抽象类(装饰器)
package decorator;
/**
* Created by noteless on 2018/9/6.
* Description:装饰器类实现了手抓饼接口,具有了手抓饼的类型
*/
public abstract class Decorator implements HandPancake{
private HandPancake handPancake;
Decorator(HandPancake handPancake){
this.handPancake = handPancake;
}
/**提供手抓饼
* @return
*/
@Override
public String offerHandPancake() {
return handPancake.offerHandPancake();
} /**提供手抓饼的价格
* @return
*/
@Override
public Integer calcCost() {
return handPancake.calcCost();
}
}
具体的配菜(具体的装饰)
package decorator;
/**
* Created by noteless on 2018/9/6.
* Description:培根
*/
public class Bacon extends Decorator {
Bacon(HandPancake handPancake){
super(handPancake);
} @Override
public String offerHandPancake() {
return super.offerHandPancake()+" 加培根";
}
@Override
public Integer calcCost() {
return super.calcCost()+4;
}
} package decorator;
/**
* Created by noteless on 2018/9/6.
* Description:鸡蛋
*/
public class Egg extends Decorator {
Egg(HandPancake handPancake){
super(handPancake);
}
@Override
public String offerHandPancake() {
return super.offerHandPancake()+"加鸡蛋";
}
@Override
public Integer calcCost() {
return super.calcCost()+2;
}
} package decorator;
/**
* Created by noteless on 2018/9/6.
* Description:烤肠
*/
public class Sausage extends Decorator {
Sausage(HandPancake handPancake){
super(handPancake);
}
@Override
public String offerHandPancake() {
return super.offerHandPancake()+" 加香肠";
}
@Override
public Integer calcCost() {
return super.calcCost()+3;
}
} package decorator;
/**
* Created by noteless on 2018/9/6.
* Description:青菜
*/
public class Vegetable extends Decorator {
Vegetable(HandPancake handPancake){
super(handPancake);
}
@Override
public String offerHandPancake() {
return super.offerHandPancake()+" 加青菜";
}
@Override
public Integer calcCost() {
return super.calcCost()+1;
} }
顾客
package decorator;
/**
* Created by noteless on 2018/9/6.
* Description:顾客具有名字,然后购买手抓饼
*/
public class Customer {
private String name;
Customer(String name){
this.name = name;
} public void buy(HandPancake handPancake){
System.out.println(name+"购买了 : "+handPancake.offerHandPancake()+
" 一份, 花了 : "+handPancake.calcCost()+"块钱~");
System.out.println();
}
}
测试类
package decorator; /**
* Created by noteless on 2018/9/6.
* Description:
* 手抓饼3块
* Sausage 烤肠 3块
* Bacon 培根 4块
* Egg 鸡蛋2块
* Vegetable 青菜 1块
*/ public class Test {
public static void main(String ...strings){ //有一个顾客张三,他想吃手抓饼了,来了一个原味的
Customer customerA = new Customer("张三");
customerA.buy(new NotelessHandPancake()); //有一个顾客李四,他想吃手抓饼了,他加了一根烤肠
Customer customerB = new Customer("李四");
customerB.buy(new Sausage(new NotelessHandPancake())); //有一个顾客王五,他想吃手抓饼了,他加了一根烤肠 又加了培根
Customer customerC = new Customer("王五");
customerC.buy(new Bacon(new Sausage(new NotelessHandPancake()))); //有一个顾客王五的兄弟,他想吃手抓饼了,他加了培根 又加了烤肠
Customer customerC1 = new Customer("王五的兄弟");
customerC1.buy(new Sausage(new Bacon(new NotelessHandPancake()))); //有一个顾客赵六,他想吃手抓饼了,他加了一根烤肠 又加了2份培根
Customer customerD = new Customer("赵六");
customerD.buy(new Bacon(new Bacon(new Sausage(new NotelessHandPancake()))));
//有一个顾客 王二麻子,他想吃手抓饼了,特别喜欢吃青菜 来了三分青菜
Customer customerE = new Customer("王二麻子");
customerE.buy(new Vegetable(new Vegetable(new Vegetable(new NotelessHandPancake()))));
//有一个顾客 有钱人 王大富 来了一个全套的手抓饼
Customer customerF = new Customer("王大富");
customerF.buy(new Egg(new Vegetable(new Bacon(new Sausage(new NotelessHandPancake())))));
}
}

UML图

手抓饼装饰器模式中的根本
上面的代码还是比较清晰的,如果你没办法仔细看进去的话,我们换一种思维方式来思考手抓饼的装饰器模式
你可以这么理解:
你过去手抓饼的摊位那边,你说老板来一个手抓饼,加培根,加鸡蛋
摊主那边是这样子的:
老板负责直接做手抓饼
旁边站着漂亮的老板娘,手里拿着手抓饼的袋子,负责帮你装袋,你总不能直接用手拿饼,对吧
接下来我们说下过程:
老板马上就开始做手抓饼了,做好了之后,老板把手抓饼交给了旁边站着的老板娘
老板娘在给装袋并且交给你之前
把鸡蛋和培根放到了你的手抓饼里面
然后又放到了包装袋子里面
接着递给了你
你说到底是老板娘手里包装好的手抓饼是手抓饼 还是老板做好的热气腾腾的是手抓饼呢?
其实,老板做好的热气腾腾的手抓饼,正是我们上面提供出来的具体的手抓饼
老板娘手里拿着的手抓饼包装袋来包装手抓饼,也是手抓饼,只不过是包装了下,这个就是装饰器的概念
所以装饰器模式还有一个名字 包装器模式(Wrapper)
|
根本: 是你还有你 |

面向对象中的适配器模式详解
意图
动态的给一个对象添加额外的职责,简单说,动态的扩展职责 就增加功能来说,装饰器模式比生成子类要更加灵活 所以装饰器模式主要解决继承子类爆炸增长的问题 |
装饰器模式中的角色
Component | 抽象构建 | 装饰器模式中必然有一个最基本最原始的-> 接口/抽象类 来充当抽象构建 |
抽象的手抓饼 HandPancake |
ConcreteComponent | 具体构建 |
是抽象构建的一个具体实现
你要装饰的就是它
|
具体某家店铺生产的手抓饼 NotelessHandPancake |
Decorator | 装饰抽象类 | 一般是一个抽象类 实现抽象构建 并且必然有一个private变量指向Component 抽象构建 |
配菜抽象类(装饰器) Decorator |
ConcreteDecorator | 具体的装饰类 | 必须要有具体的装饰角色 否则装饰模式就毫无意义了 |
具体的配菜(具体的装饰) Bacon Egg Vegetable Sausage |

仔细体味下<是你 还有你>
Decorator 是Component 还有Component
|
类应该对扩展开放,对修改关闭 |
所谓修改就是指继承,一旦继承,那么将会对部分源代码具有修改的能力,比如覆盖方法,所以你尽量不要做这件事情 扩展就是指的组合,组合不会改变任何已有代码,动态得扩展功能 |
装饰器模式优点
装饰类和被装饰类可以独立发展,而不会相互耦合
Component类无须知道Decorator类,Decorator类是从外部来扩展Component类的功能,
而Decorator也不用知道具体的构件
|
装饰模式是继承关系的一个替代方案
我们看装饰类Decorator,不管装饰多少层,他始终是一个Component,实现的还是is-a的关系,所以他是继承的一种良好替代方案
|
如果设计得当,装饰器类的嵌套顺序可以任意,比如![]() 一定要注意前提,那就是你的装饰不依赖顺序 |
装饰器模式缺点
装饰器模式虽然从数量级上减少了类的数量,但是为了要装饰,仍旧会增加很多的小类 这些具体的装饰类的逻辑将不会非常的清晰,不够直观,容易令人迷惑 |
装饰器模式虽然减少了类的爆炸,但是在使用的时候,你就可能需要更多的对象来表示继承关系中的一个对象 |
多层的装饰是比较复杂,比如查找问题时,被层层嵌套,不容易发现问题所在 |
装饰器模式使用场景
当你想要给一个类增加功能,然而,却并不想修改原来类的代码时,可以考虑装饰器模式 如果你想要动态的给一个类增加功能,并且这个功能你还希望可以动态的撤销,就好像直接拿掉了一层装饰物 |
装饰器模式的简化变形
如果省略抽象构建,装饰器直接装饰一个类的话, 那么可以装饰器直接继承这个类
|
如果只有一个具体的装饰器类,那么可以省略掉 Decorator ConcreteDecorator 充当了ConcreteDecorator 和 Decorator的角色 ![]() |
设计模式是作为解决问题或者设计类层级结构时的一种思维的存在,而不是公式一样的存在!
|
装饰器模式 Decorator 结构型 设计模式 (十)的更多相关文章
- Java设计模式07:常用设计模式之装饰器模式(结构型模式)
1. Java之装饰器模式(Decorator Pattern) (1)概述: 装饰模式在Java种使用也很广泛,比如我们在重新定义按钮.对话框等时候,实际上已经在使用装饰模式了.在不必改变原 ...
- 设计模式(八)装饰器模式Decorator(结构型)
设计模式(八)装饰器模式Decorator(结构型) 1. 概述 若你从事过面向对象开发,实现给一个类或对象增加行为,使用继承机制,这是所有面向对象语言的一个基本特性.如果已经存在的一个类缺少某些方法 ...
- 桥接模式 桥梁模式 bridge 结构型 设计模式(十二)
桥接模式Bridge Bridge 意为桥梁,桥接模式的作用就像桥梁一样,用于把两件事物连接起来 意图 将抽象部分与他的实现部分进行分离,使得他们都可以独立的发展. 意图解析 依赖倒置原 ...
- 享元模式 FlyWeight 结构型 设计模式(十五)
享元模式(FlyWeight) “享”取“共享”之意,“元”取“单元”之意. 意图 运用共享技术,有效的支持大量细粒度的对象. 意图解析 面向对象的程序设计中,一切皆是对象,这也就意味着系统的运行将 ...
- 【PHP设计模式 09_ZhuangShiQi.php】装饰器模式 (decorator)
<?php /** * [装饰器模式 (decorator)] * 有时候发布一篇文章需要经过很多人手,层层处理 */ header("Content-type: text/html; ...
- 装饰器模式-Decorator(Java实现)
装饰器模式-Decorator(Java实现) 装饰器模式允许向一个现有的对象添加新的功能, 同时又不改变其结构. 其中 "现有对象"在本文中是StringDisplay类. 添加 ...
- 组合模式 合成模式 COMPOSITE 结构型 设计模式(十一)
组合模式(合成模式 COMPOSITE) 意图 将对象组合成树形结构以表示“部分-整体”的层次结构. Composite使得用户对单个对象和组合对象的使用具有一致性. 树形结构介绍 为了便于理解, ...
- 12、Decorator 装饰器 模式 装饰起来美美哒 结构型设计模式
1.Decorator模式 装饰模式又名包装(Wrapper)模式.装饰模式以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案. 装饰器模式(Decorator Pattern)允许向一个现 ...
- 设计模式学习心得<装饰器模式 Decorator>
装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构.这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装. 这种模式创建了一个装饰类,用来包装 ...
随机推荐
- json字符串的拼接
关于json字符串的解析与拼接,第一次接触,留下个笔记了.......解析,是改的代码,拼接是纯的,解析就不说了,笔记一下拼接了 关于解析主要分三部分,一个是第一层处理,一个是第二层处理,一个是进行& ...
- go 结构体
结构体声明 type Employee struct { ID int Name string Address string DoB time.Time Position string Salary ...
- es6的基本数据详解
一.Set 基本用法: 1)ES6提供了新的数据机构-Set. 它类似于数组,但是成员的值都是唯一的,没有重复的值.Set本身是一个构造函数,用来生成Set数据结构. 先来看一段最简单的代码: 1 ...
- django+javascrpt+python实现私有云盘代码
丁丁:由于篇幅有限,这里暂时只展示python后端代码,前端js代码后面上传,有需要的也可以留言私信我. 1.view.py 使用用户.部门.公司等相关账号的创建,已经个人,部门账号的冻结,删除,相关 ...
- su;su -;sudo;sudo -i;sudo su;sudo su - 之间的区别
今天我们来聊聊su;su -;sudo;sudo -i;sudo su;sudo su -他们之间的区别. su :su 在不加任何参数,默认为切换到root用户,但没有转到root用户家目录下,也就 ...
- Oracle 的开窗函数 rank,dense_rank,row_number
1.开窗函数和分组函数的区别 分组函数是指按照某列或者某些列分组后进行某种计算,比如计数,求和等聚合函数进行计算. 开窗函数是指基于某列或某些列让数据有序,数据行数和原始数据数相同,依然能曾现个体数据 ...
- CS231n 第一次作业KNN中本地CIFAR10数据集的载入
一.问题描述 网上绝大多数作业参考都是在jupyter下运行的,数据集载入过程一般如下: from cs231n.data_utils import load_CIFAR10 #导入数据集,并打印出数 ...
- [Swift]LeetCode963. 最小面积矩形 II | Minimum Area Rectangle II
Given a set of points in the xy-plane, determine the minimum area of any rectangle formed from these ...
- 分布式事务之如何基于RocketMQ的事务消息特性实现分布式系统的最终一致性?
导读 在之前的文章中我们介绍了如何基于RocketMQ搭建生产级消息集群,以及2PC.3PC和TCC等与分布式事务相关的基本概念(没有读过的读者详见
- Python必备库
Python必备库 --default-timeout=100避免网络延迟错误:-U给管理员权限. Python基础库 pip --default-timeout=100 install -U pyg ...