装饰模式
1、概念
(1)什么是装饰模式?
装饰模式指的是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。
(2)为什么用装饰模式?
需要对原有对象的功能进行扩展的时候、需要动态地给一个对象增加功能,并可以动态地撤销时、需要为一批的兄弟类进行改装或加装功能时。
一般来说扩展一个对象的功能的方式有三种:继承、装饰器模式、代理模式。
继承:也可以通过继承父类、子类扩展的方式对功能进行扩展。缺点是
1、无法获取父类的私有属性,无法进行扩展。(如实现简易JDBC连接池的时候,重写close方法无法将连接connection释放到简易连接池中,而且得到connection时绑定了数据源的信息,子类无法得到对应信息,无法调用close方法)。
2、继承逻辑固定,需求变更时不便于维护。需求变动大的话,继承方式进行扩展时,类数据量会激增,不便于维护;而且继承的层次也可能比较多。由于继承的结构已经固定,需求变更时,代码维护困难。
装饰器模式:相较于继承来说,装饰类和被装饰类是可以独立的,耦合性降低。装饰模式是继承的一种替换方案,无论包装多少层,返回的对象都是原始待被装饰对象(如下例返回的都是DecoratorCase类型)。
3、代理模式。代理模式和装饰模式相同点都是为了对对象进行增强。代理模式出发点是对代理的对象进行增强;装饰模式是自己对自己的功能进行增强。
不同点是:
(1)、装饰器模式强调的是增强自身,在被装饰之后你能够在被增强的类上使用增强后的功能。增强后你还是你,只不过能力更强了而已;代理模式强调要让别人帮你去做一些本身与你业务没有太多关系的职责(记录日志、设置缓存)。代理模式是为了实现对象的控制,因为被代理的对象往往难以直接获得或者是其内部不想暴露出来。
(2)、装饰模式是以对客户端透明的方式扩展对象的功能,是继承方案的一个替代方案;代理模式则是给一个对象提供一个代理对象,并由代理对象来控制对原有对象的引用;
(3)、装饰模式是为装饰的对象增强功能;而代理模式对代理的对象施加控制,但不对对象本身的功能进行增强;
(3)装饰模式实现方式
本质还是通过继承的方式实现,是继承的一种替代方案。
2、装饰模式的实现
待实现的功能:原有功能是早上起来去上班,现需要对原有功能进行增强,即去上班前吃早餐或看天气预报,或者上班出发后吃早餐或看天气预报。
第一步:定义接口
package com.example.designmode.decorator.impl;
public interface DecoratorCase {
void goToWork();
}
第二步:定义基础实现类
@Slf4j
public class DecoratorCaseImpl implements DecoratorCase {
@Override
public void goToWork() {
log.info("去上班");
}
}
第三步:定义装饰类(一般为抽象类)
package com.example.designmode.decorator.impl;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public abstract class AbstractDecoratorCase implements DecoratorCase {
private DecoratorCase decoratorCase;
public AbstractDecoratorCase(DecoratorCase decoratorCase) {
this.decoratorCase = decoratorCase;
}
@Override
public void goToWork() {
if (decoratorCase != null) {
decoratorCase.goToWork();
}
}
}
第四步:定义增强类(继承装饰类)
package com.example.designmode.decorator.impl;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class DecoratorCaseEatImpl extends AbstractDecoratorCase {
public DecoratorCaseEatImpl(DecoratorCase decoratorCase) {
super(decoratorCase);
}
@Override
public void goToWork() {
eat();
super.goToWork();
}
private void eat() {
log.info("喝早餐牛奶");
}
}
package com.example.designmode.decorator.impl;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class DecoratorCaseEatBreadImpl extends AbstractDecoratorCase {
public DecoratorCaseEatBreadImpl(DecoratorCase decoratorCase) {
super(decoratorCase);
}
@Override
public void goToWork() {
eatBread();
super.goToWork();
}
private void eatBread() {
log.info("吃早餐面包");
}
}
package com.example.designmode.decorator.impl;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class DecoratorCaseWatchHometownWeatherImpl extends AbstractDecoratorCase {
public DecoratorCaseWatchHometownWeatherImpl(DecoratorCase decoratorCase) {
super(decoratorCase);
}
@Override
public void goToWork() {
super.goToWork();
watchWeather();
}
private void watchWeather() {
log.info("看下老家今天的天气预报");
}
}
package com.example.designmode.decorator.impl;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class DecoratorCaseWatchWeatherImpl extends AbstractDecoratorCase {
public DecoratorCaseWatchWeatherImpl(DecoratorCase decoratorCase) {
super(decoratorCase);
}
@Override
public void goToWork() {
super.goToWork();
watchWeather();
}
private void watchWeather() {
log.info("看下今天天气预报");
}
}
第五步:测试
public static void main(String[] args) {
DecoratorCaseWatchHometownWeatherImpl decoratorCaseWatchHometownWeather = new DecoratorCaseWatchHometownWeatherImpl(
new DecoratorCaseWatchWeatherImpl(new DecoratorCaseEatBreadImpl(
new DecoratorCaseEatImpl(new DecoratorCaseImpl()))));
decoratorCaseWatchHometownWeather.goToWork();
log.info("\n");
DecoratorCaseWatchWeatherImpl decoratorCaseWatchWeather = new DecoratorCaseWatchWeatherImpl(
new DecoratorCaseWatchHometownWeatherImpl(new DecoratorCaseEatImpl(
new DecoratorCaseEatBreadImpl(new DecoratorCaseImpl()))));
decoratorCaseWatchWeather.goToWork();
}
//注:可看出,执行结果与装饰器装入顺序有关,在原有功能之前增强的功能,先装入后执行,在原有功能之后增强的功能,先装入先执行。
(1)、通过例子可看出,需求变更时,只需要改变装饰器的装入顺序即可。但继承却需要去修改原有代码逻辑。
(2)、如果出门上班前后的需求需要反过来的话,需要在装饰器中进行修改。注意,这种修改是合理的,因为我的装饰类没有指定装饰类的执行顺序,只是指定了对原有功能的增强顺序。而继承不一样,它的功能扩展是有逻辑的,如先继承DecoratorCase->实现DecoratorCaseEatImpl->再继承DecoratorCaseEatImpl实现DecoratorCaseWatchWeatherImpl,这是固定的,在继承的逻辑上已经写死了,十分不便于修改。
(3)、可看出,执行结果与装饰器装入顺序有关,在原有功能之前增强的功能,先装入后执行,在原有功能之后增强的功能,先装入先执行。
3、总结
平常当系统需要新功能时,是向旧的类中添加新的代码,这些新加的代码通常装饰了原有类的核心职责或主要行为,这种做法的问题在于,它们再主类中加入了新的字段、新的方法和新的逻辑,从而增加了主类的复杂度,而这些新加入的东西仅仅是为了满足一些只在某种特定情况下才会执行的个特殊行为的需要。
而装饰模式却提供了一个非常好的解决方案,它把每个要装饰的功能放在单独的类中,并让这个类包装它所要装饰的对象。因此当需要执行特殊行为时,客户代码就可以在运行时根据需要有选择地、按顺序的地使用装饰功能包装对象了。
参考博客:
https://www.cnblogs.com/adamjwh/p/9036358.html