- 导语
- 怎么用
1.样例背景
2.UML类图
3.代码示例
3.1 不用设计模式时,以及此时存在的问题
3.2 使用设计模式后的情况- 优缺点
- 使用场景
1.概括描述
2.现存知名产品中的使用示例- 与其他设计模式的对比
- 参考
导语
装饰(者)模式(Decorator,也叫包装器模式),在不改变原有对象的基础之上,动态的将功能附加到对象上。
提供了比继承更有弹性的替代方案(扩展原有对象的功能)
如果比较全的话要有以下几个角色:
1.抽象被装饰者
2.实际被装饰者
3.抽象装饰者
4.实际装饰者
特别注意:装饰模式的装饰顺序很重要,比如加密数据
和过滤词汇
都可以是数据持久化之前的装饰功能,但若是先加密了数据后过滤词汇就会出问题,最理想的情况,是保证装饰类之间彼此独立,这样他们就可以以任意顺序组合了。
怎么用
共有2个示例,代码详见访问链接
下面以example1举例说明
1. 样例背景
吃早点,买煎饼
有的人愿意加个鸡蛋,有的人愿意加两个鸡蛋,有的人愿意加火腿,有的人……
2. UML类图
AbstractBatterCake -------------- 抽象被装饰者
BatterCake ---------------- 实际被装饰者
BatterCakeDecorator ------ 抽象装饰者(这里只是没有定义成抽象类)
SausageDecorator ----------------- 实际装饰者
EggDecorator ----------------- 实际装饰者
3. 代码示例
3.1 使用装饰模式之前
/**
* @Description: 煎饼
*/
public class BatterCake {
protected String getDesc(){
return "煎饼";
}
protected int cost(){
return 8;
}
}
/**
* @Description: 加了鸡蛋的煎饼
*/
public class BatterCakeWithEgg extends BatterCake {
@Override
protected String getDesc() {
return super.getDesc() + "加了一个鸡蛋";
}
@Override
protected int cost() {
return super.cost() + 2;
}
}
/**
* @Description: 加了鸡蛋和火腿的煎饼
*/
public class BatterCakeWithEggSausage extends BatterCake {
@Override
protected String getDesc() {
return super.getDesc() + "加了一个鸡蛋,又加了一个火腿";
}
@Override
protected int cost() {
return super.cost() + 4;
}
}
/**
* @Description: 测试类
*/
public class Test {
public static void main(String[] args) {
beforeDecorator();
}
/**
* @Description: 使用装饰者模式之前
* 在现有条件下只能满足这两种需求
* 如果客户还有别的需求怎么办?比如,需要加5个火腿2个鸡蛋
*/
public static void beforeDecorator(){
// 要一个加鸡蛋的煎饼
BatterCake batterCake1 = new BatterCakeWithEgg();
System.out.println(batterCake1.getDesc() + " 销售价格:" + batterCake1.cost());
// 要一个加鸡蛋和一个火腿的煎饼
BatterCake batterCake2 = new BatterCakeWithEggSausage();
System.out.println(batterCake2.getDesc() + " 销售价格:" + batterCake2.cost());
}
}
3.2 使用装饰模式之后
/**
* @Description: 煎饼
*/
public abstract class AbstractBatterCake {
protected abstract String getDesc();
protected abstract int cost();
}
/**
* @Description: 具体煎饼(具体的被装饰类)
*/
public class BatterCake extends AbstractBatterCake{
@Override
protected String getDesc(){
return "煎饼";
}
@Override
protected int cost(){
return 8;
}
}
/**
* @Description: 抽象的装饰者类
* 因为你不知道将来的客户到底需要什么样的煎饼
*
* 如果 那些具体的装饰类中,有一些基础的共性的动作,那么可以把该类定义为抽象类,
* 并添加基础公共的方法
*/
public class BatterCakeDecorator extends AbstractBatterCake{
private AbstractBatterCake abstractBatterCake;
public BatterCakeDecorator(AbstractBatterCake abstractBatterCake) {
this.abstractBatterCake = abstractBatterCake;
}
@Override
protected String getDesc() {
return abstractBatterCake.getDesc();
}
@Override
protected int cost() {
return abstractBatterCake.cost();
}
}
/**
* @Description: 加了鸡蛋的装饰类(具体的装饰类)
*/
public class EggDecorator extends BatterCakeDecorator {
public EggDecorator(AbstractBatterCake abstractBatterCake) {
super(abstractBatterCake);
}
@Override
protected String getDesc() {
return super.getDesc() + " 加了一个鸡蛋";
}
@Override
protected int cost() {
return super.cost() + 2;
}
}
/**
* @Description: 加了火腿的装饰类(具体的装饰类)
*/
public class SausageDecorator extends BatterCakeDecorator {
public SausageDecorator(AbstractBatterCake abstractBatterCake) {
super(abstractBatterCake);
}
@Override
protected String getDesc() {
return super.getDesc() + " 加了一个火腿";
}
@Override
protected int cost() {
return super.cost() + 2;
}
}
/**
* @Description: 测试类
* @Author: zqy
* @CreateTime: 2019-07-20 19:55
*/
public class Test {
public static void main(String[] args) {
afterDecorator();
}
/**
* @Description: 采用装饰者模式后
* 以不变应万变
*/
public static void afterDecorator(){
AbstractBatterCake batterCakeBase = new BatterCake();
// 要一个加鸡蛋的煎饼
AbstractBatterCake batterCake1 = new EggDecorator(batterCakeBase);
System.out.println(batterCake1.getDesc() + " 销售价格:" + batterCake1.cost());
// 要一个加鸡蛋和一个火腿的煎饼
AbstractBatterCake batterCake2 = new SausageDecorator(
new EggDecorator(batterCakeBase));
System.out.println(batterCake2.getDesc() + " 销售价格:" + batterCake2.cost());
// 要二个加鸡蛋和一个火腿的煎饼
AbstractBatterCake batterCake3 = new SausageDecorator(
new EggDecorator(
new EggDecorator(batterCakeBase)));
System.out.println(batterCake3.getDesc() + " 销售价格:" + batterCake3.cost());
}
}
优缺点
缺点
1.会出现更多的代码,更多的类,增加程序的复杂性
2.动态装饰时,多层装饰时会更复杂优点
1.不改变原有对象的情况下给一个对象扩展功能——继承的有力补充,比继承灵活
2.通过使用不同的装饰类以及他们的排列组合,可以实现多个不同的效果
3.有效的把类的核心职责和装饰功能区分开,而且可以去除相关类中重复的装饰逻辑
4.符合开闭原则
使用场景
1. 概括描述
- 扩展一个类的功能或给一个类添加附加职责
2. 现存知名产品中的使用示例 todo
2.1 java.io.BufferedReader (jdk)
2.2 java.io.BufferedInputStream(jdk)
2.3 org.springframework.cache.transaction.TransactionAwareCacheDecorator
(spring)
2.4 org.apache.ibatis.cache.Cache (mybatis)
相关设计模式
装饰模式和代理模式
装饰模式关注在一个对象上动态的添加方法;
代理模式关注于控制对对象的访问;
使用上二者的不同:
代理模式
中的代理类,可以对他的客户隐藏一个对象的具体信息,我们通常在使用代理模式的时候,常常在一个代理类中创建一个对象的实例,而当我们使用装饰模式
的时候,我们通常会把原始对象作为一个参数,传给装饰者的构造器,这个是使用上的一些不同。
从结构上来看和装饰模式
类似,但代理模式
是控制,更像是一种对功能的限制,而装饰模式
是增加职责。装饰模式和适配器模式
他们都可以叫做包装模式(wrapper)
装饰者和被装饰者可以实现相同的接口,或者装饰者是被装饰者的子类
在适配器模式中,适配器和被适配的类具有不同的接口,当然也有可能是有部分接口是重合的。
(如果在深入一点,装饰者模式还可以退化成半装饰者模式,也就是说,一个装饰者除了提供被装饰类的接口外,还提供了其他的方法,那就变成了一个半透明的装饰者,如果应用层代码想使用这个提供了特殊功能的方法的话,就要使用具体的装饰者类了。半装饰者在实际的场景中应用的比较少,重点关注装饰者就可以了。)
参考
- https://coding.imooc.com/learn/list/270.html(强烈推荐)
- https://en.wikipedia.org/wiki/Design_Patterns
- 大话设计模式,电子版下载链接,https://pan.baidu.com/s/17WOI3Bvp-JUoQXvaomHISg 密码:vw05
(作者博客,https://www.cnblogs.com/cj723/archive/2007/12/30/1021314.html) - https://www.cnblogs.com/geek6/p/3951677.html
- https://mp.weixin.qq.com/s/eAlPRqScG3-Acvi3HwYK3A