【设计模式】29.结构型模式-装饰模式(Decorator)
一、描述
装饰模式能够在不改变原来对象结构和功能的前提下,动态的给对象增加新的功能,相比于使用子类扩展的方式,装饰模式更加的灵活。
角色
(1)抽象构件类:为具体构件类和装饰类提供抽象方法。
(2)具体构建类:是抽象构建类的子类,实现抽象方法,用于定义具体的构件对象。
(3)抽象装饰类:是抽象构件类的子类,维护了一个指向抽象构件类的引用,同时提供构造方法或者set方法设置具体构件类。提供了设置装饰的抽象方法。
(4)具体装饰类:是抽象装饰类的子类,实现了其装饰的抽象方法。负责向构建中添加新的职责,每一个具体装饰类都定义了一些新的行为,可以调用抽象装饰类中定义的方法,并可以增加新的职责用以扩充对象的行为。
类图
二、优点
(1)装饰者模式和继承关系的目的都是要扩展对象的功能,但是装饰模式可以提供比继承更多的灵活性
(2)通过使用不同的具体装饰者类及他们不同的组合顺序,可以得到不同装饰后具有不同行为或者状态的对象
(3)符合开闭原则(对扩展开放,对修改关闭)
(4)动态扩展类功能,比类继承灵活,且对客户端透明
(5)继承关系的一种替代方案,相比于类继承的父子关系,装饰模式更像是一种组合关系(is-a)
(6)可以对同一个被装饰对象进行多次装饰,创建出不同行为的符合功能
三、缺点
(1)多层装饰比较复杂
(2)装饰嵌套过多,会产生过多小对象(每个装饰层都创建一个相应的对象)
(3)装饰嵌套过多,易于出错,且调试排查比较麻烦(需要一层一层对装饰器进行排查,以确定是哪一个装饰层出错)
四、使用场景
(1)需要扩展一个类的功能,或给一个类增加附加功能
(2)需要动态地给一个对象增加功能,且这些功能可以再动态的撤销
(3)需要为一批兄弟类进行改装或加装功能
五、示例
以手机为例,从刚开始的基础版手机只具备打电话、发短信的功能,到后来可以安装QQ、浏览网页的功能,再到现在的智能手机可以安装各种各样的app,堪比一个小型电脑。手机的发展可以看做一个装饰者模式,以打电话、发短信为最基本的功能,QQ、浏览器、微信、王者荣耀等可以动态添加到手机的功能中。
类图
代码
(1)手机
public abstract class MobilePhone {
/**
* 抽象操作方法
*/
abstract void operation();
}
(2)基础功能手机
public class FirstGenerationPhone extends MobilePhone {
@Override
void operation() {
call();
sms();
}
private void call() {
System.out.println("打电话");
}
private void sms() {
System.out.println("发短信");
}
}
(3)抽象装饰类
定义如何添加手机的附加功能
public class Decorate extends MobilePhone{
private MobilePhone mobilePhone;
public Decorate(MobilePhone mobilePhone){
this.mobilePhone = mobilePhone;
}
@Override
void operation() {
mobilePhone.operation();
}
}
(4)QQ
添加QQ软件,除基础功能之外,添加QQ聊天功能
public class QQDecorate extends Decorate{
public QQDecorate(MobilePhone mobilePhone) {
super(mobilePhone);
}
@Override
void operation() {
super.operation();
qq();
}
private void qq(){
System.out.println("qq聊天");
}
}
(4)WeChat
添加微信软件
public class WeChatDecorate extends Decorate {
public WeChatDecorate(MobilePhone mobilePhone) {
super(mobilePhone);
}
@Override
void operation() {
super.operation();
chat();
}
private void chat() {
System.out.println("微信聊天");
}
}
(5)ArenaGame
添加王者荣耀软件
public class ArenaGameDecorate extends Decorate{
public ArenaGameDecorate(MobilePhone mobilePhone) {
super(mobilePhone);
}
@Override
void operation() {
super.operation();
play();
}
private void play(){
System.out.println("玩儿王者荣耀");
}
}
(6)Client
实现
public class Client {
public static void main(String[] args) {
System.out.println("第一代手机");
FirstGenerationPhone phone = new FirstGenerationPhone();
phone.operation();
System.out.println("\n安装QQ之后的功能");
QQDecorate qqDecorate = new QQDecorate(phone);
qqDecorate.operation();
System.out.println("\n安装微信之后的功能");
WeChatDecorate weChatDecorate = new WeChatDecorate(qqDecorate);
weChatDecorate.operation();
System.out.println("\n安装王者荣耀之后的功能");
ArenaGameDecorate arenaGameDecorate = new ArenaGameDecorate(weChatDecorate);
arenaGameDecorate.operation();
}
}
效果:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用