设计模式之装饰模式
时间:2022-04-26
本文章向大家介绍设计模式之装饰模式,主要内容包括1.装饰模式简介、定义、装饰模式结构图、2.装饰模式的简单实现、组件具体实现类(ConcreteComponent)、抽象装饰者(Decorator)、装饰者具体实现类(ConcreteDecorator)、客户端调用、3.装饰模式的优缺点和使用场景、缺点、使用场景、4.装饰模式和代理模式、基本概念、基础应用、原理机制和需要注意的事项等,并结合实例形式分析了其使用技巧,希望通过本文能帮助到大家理解应用这部分内容。
相关文章 设计模式之设计六大原则 设计模式之单例模式的七种写法 设计模式之建造者模式 设计模式之简单工厂模式 设计模式之观察者模式 设计模式之代理模式
1.装饰模式简介
装饰模式介绍
装饰模式是结构型设计模式之一,不必改变类文件和使用继承的情况下,动态地扩展一个对象的功能,是继承的替代方案之一。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。
定义
动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活。
装饰模式结构图
- Component:抽象组件,给对象动态的添加职责。
- ConcreteComponent:组件具体实现类。
- Decorator:抽象装饰者,继承Component,从外类来拓展Component类的功能,但对于Component来说无需知道Decorator的存在。
- ConcreteDecorator:装饰者具体实现类。
2.装饰模式的简单实现
装饰模式在现实生活中有很多例子,比如给一个人穿上各种衣服,给一幅画涂色上框等等,这次我要举得例子有些不同,举一个武侠修炼武功的例子:杨过本身就会全真剑法,有两位武学前辈洪七公和欧阳锋分别传授杨过打狗棒法和蛤蟆功,这样杨过除了会全真剑法还会打狗棒法和蛤蟆功。
抽象组件(Component)
作为武侠肯定要会使用武功的,我们先定义一个武侠的抽象类,里面有使用武功的抽象方法:
public abstract class Swordsman {
/**
* Swordsman武侠有使用武功的抽象方法
*/
public abstract void attackMagic();
}
组件具体实现类(ConcreteComponent)
被装饰的具体对象,在这里就是被教授武学的具体的武侠,他就是杨过,杨过作为武侠当然也会武学,虽然不怎么厉害:
public class YangGuo extends Swordsman{
@Override
public void attackMagic() { //杨过初始的武学是全真剑法
System.out.println("杨过使用全真剑法");
}
}
抽象装饰者(Decorator)
抽象装饰者保持了一个对抽象组件的引用,方便调用被装饰对象中的方法。在这个例子中就是武学前辈要持有武侠的引用,方便教授他武学并“融会贯通”:
public abstract class Master extends Swordsman{
private Swordsman mSwordsman; public Master(Swordsman mSwordsman){ this.mSwordsman=mSwordsman;
} @Override
public void attackMagic() {
mSwordsman.attackMagic();
}
}
装饰者具体实现类(ConcreteDecorator)
这个例子中用两个装饰者具体实现类,分别是洪七公和欧阳锋,他们负责来传授杨过新的武功:
public class HongQiGong extends Master {
public HongQiGong(Swordsman mSwordsman) { super(mSwordsman);
} public void teachAttackMagic(){
System.out.println("洪七公教授打狗棒法");
System.out.println("杨过使用打狗棒法");
} @Override
public void attackMagic() { super.attackMagic();
teachAttackMagic();
}
}
public class OuYangFeng extends Master {
public OuYangFeng(Swordsman mSwordsman) { super(mSwordsman);
} public void teachAttackMagic(){
System.out.println("欧阳锋教授蛤蟆功");
System.out.println("杨过使用蛤蟆功");
} @Override
public void attackMagic() { super.attackMagic();
teachAttackMagic();
}
}
客户端调用
经过洪七公和欧阳锋的教导,杨过除了初始武学全真剑法又学会了打狗棒法和蛤蟆功:
public class Client { public static void main(String[] args){ //创建杨过
YangGuo mYangGuo=new YangGuo(); //洪七公教授杨过打狗棒法,杨过会了打狗棒法
HongQiGong mHongQiGong=new HongQiGong(mYangGuo);
mHongQiGong.attackMagic(); //欧阳锋教授杨过蛤蟆功,杨过学会了蛤蟆功
OuYangFeng mOuYangFeng=new OuYangFeng(mYangGuo);
mOuYangFeng.attackMagic();
}
}
3.装饰模式的优缺点和使用场景
优点
- 通过组合而非继承的方式,动态的来扩展一个对象的功能,在运行时选择不同的装饰器,从而实现不同的行为。
- 有效避免了使用继承的方式扩展对象功能而带来的灵活性差,子类无限制扩张的问题。
- 具体组件类与具体装饰类可以独立变化,用户可以根据需要增加新的具体组件类和具体装饰类,在使用时再对其进行组合,原有代码无须改变,符合“开闭原则”。
缺点
- 装饰链不能过长,否则会影响效率。
- 因为所有对象都是继承于Component,所以如果Component内部结构发生改变,则不可避免地影响所有子类(装饰者和被装饰者),如果基类改变,势必影响对象的内部。
- 比继承更加灵活机动的特性,也同时意味着装饰模式比继承更加易于出错,排错也很困难,对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为烦琐,所以只在必要的时候使用装饰者模式。
使用场景
- 在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
- 需要动态地给一个对象增加功能,这些功能可以动态的撤销。
- 当不能采用继承的方式对系统进行扩充或者采用继承不利于系统扩展和维护时。
4.装饰模式和代理模式
在上一篇文章设计模式之代理模式中我们讲到了代理模式,你会觉得代理模式和装饰模式有点像,都是持有了被代理或者被装饰对象的引用。它们两个最大的不同就是装饰模式对引用的对象增加了功能,而代理模式只是对引用对象进行了控制却没有对引用对象本身增加功能。
- Quartz.net官方开发指南 第三课:更多关于Jobs和JobDetails
- 为你的WordPress 主题添加结构化数据/丰富文本摘要,高亮搜索结果(下)
- Quartz.net官方开发指南 第四课:关于Triggers更多内容
- 数据分析:寻找Python最优计算性能
- 事件流处理框架NEsper for .NET
- Quartz.net官方开发指南 第五课: SimpleTrigger
- SQL Server Performance Dashboard Reports
- 添加WordPress评论输入邮箱实时显示Gravatar头像功能
- Quartz.net官方开发指南 第六课 : CronTrigger
- WordPress 中禁止某个用户在线编辑主题
- Quartz.net官方开发指南 第七课 : TriggerListeners和JobListeners
- Quartz.net官方开发指南 第八课:SchedulerListeners
- 为WordPress 后台编辑器文本模式(HTML模式)添加按钮
- 360安全扫描之WordPress 页面异常导致本地路径泄漏 的漏洞修补
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 【python-leetcode46-子集】全排列
- 【python-leetcode784-子集】字母大小写全排列
- 悬挂引用是如何被Rust消灭的?
- python小例子(二)
- 面试题系列第1篇:说说==和equals的区别?你的回答可能是错误的
- django-URL转换器(四)
- 【猫狗数据集】加载保存的模型进行测试
- 【猫狗数据集】划分验证集并边训练边验证
- 【猫狗数据集】使用学习率衰减策略并边训练边测试
- 面试题系列第2篇:new String()创建几个对象?有你不知道的
- spring之整合struts2
- django-URL之include函数(五)
- springmvc之使用ModelAttribute避免不允许被修改的值更新时为空
- 【colab pytorch】使用tensorboard可视化
- django-URL别名的作用(六)