设计模式 10 装饰器模式
装饰器模式(Decorator Pattern)属于结构型模式
概述
装饰,顾名思义,就是在原有基础上增添东西以显示更好的效果。
生活中非常多这样的例子,衣服饰品、珠宝首饰、房子装修都是在进行装饰。
开发中这样的情况也非常多,比如用户的性别在数据库表中一般都是存的编码,然后通过字典表进行翻译,进而在页面展示为男、女。
代码实现
这里以用户服务的查询为例介绍装饰器模式:
1、定义用户服务
/**
* 用户服务
*/
public interface UserService {
/**
* 查询
*/
public void query();
}
2、实现用户服务
/**
* 用户服务实现类
*/
public class UserServiceImpl implements UserService {
@Override
public void query() {
System.out.println("查询了一个用户");
}
}
3、定义装饰器
/**
* 装饰器
*/
public class Decorator implements UserService{
/**
* 用户服务<br>
* 定义为 protected 是为了方便子类的后置处理器使用
*/
protected UserService userService;
/**
* 装饰用户服务
* @param userService 用户服务
*/
public Decorator(UserService userService) {
this.userService = userService;
}
@Override
public void query() {
userService.query();
}
}
4、定义装饰器后置处理器
/**
* 装饰器后置处理器
*/
public class DecoratorPostProcessor extends Decorator{
/**
* 使用父类的装饰器装饰用户服务
* @param userService 用户服务
*/
public DecoratorPostProcessor(UserService userService) {
super(userService);
}
@Override
public void query() {
userService.query();
// 后置处理
System.out.println("查找字典表给性别赋值");
}
}
5、调用
// 装饰用户服务实现类(注意,这里需要用装饰器后置处理器装饰,因为后置处理的业务逻辑写在里面)
Decorator decorator = new DecoratorPostProcessor(new UserServiceImpl());
// 调用装饰后的查询方法
decorator.query();
输出结果:
查询了一个用户
查找字典表给性别赋值
6、嵌套使用
在开发场景中,有时候一次后置处理并不能得到预期的数据(比如数字转化为编码,编码再转化为名称),需要再次处理,此时再去定义一个装饰器是没有必要的,可以嵌套使用。
// 嵌套使用
DecoratorPostProcessor decorator1 = new DecoratorPostProcessor(decorator);
decorator1.query();
输出结果:
查询了一个用户
查找字典表给性别赋值
查找字典表给性别赋值
这样只需要在后置处理做判断需要处理的为数字还是编码即可实现嵌套装饰的效果。
与代理模式区别
可以发现,装饰器模式与前面介绍的代理模式在结构上是一样的。对装饰器模式来说,装饰者和被装饰者都实现同一个接口或抽象类;对代理模式来说,代理类和被代理的类都实现同一个接口或抽象类,在结构上确实没有区别。
但是它们的作用不同。装饰器模式强调的是增强自身,在被装饰之后你能够在被增强的类上使用增强后的功能,增强后你还是你,只是被强化了;代理模式强调要让别人帮你去做事情,以及添加一些与你本身的业务没有太多关系的事情(记录日志、设置缓存等),重点在于让别人帮你做。
因此,装饰模式和代理模式的不同之处不在于实现方式,在于用途和思想。
优缺点
优点
装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。
缺点
多层装饰比较复杂。
使用场景
1、扩展一个类的功能。
2、动态增加功能,动态撤销。
注意事项
可代替继承。
参考
https://www.bilibili.com/video/BV1u3411P7Na?p=17&vd_source=299f4bc123b19e7d6f66fefd8f124a03