装饰模式(结构)
简介
装饰者模式是一种结构型设计模式,它允许你动态地向对象添加新的行为而不影响其原有的行为。它在运行时给对象动态地添加一些额外的职责,通常是在原有的行为基础上,通过装饰器进行一些修饰,实现了更加灵活的代码复用和扩充。
结构
抽象构件(Component):要求装饰者对象都实现一个抽象接口或抽象类。(接口或抽象类)
具体构件(ConcreteComponent):定义了一个具体的对象,也就是需要被装饰的对象。
抽象装饰器(Decorator):持有一个真实的构建对象的引用,并且可以动态给该对象添加新的功能。(接口或抽象类)
具体装饰器(ConcreteDecorator):实现具体的装饰器,向真实的构建对象添加新功能。
优缺点
优点:
非常灵活且可扩展,能动态地为对象添加新的职责和行为。
遵循开闭原则,能够实现代码的可维护性和可扩展性。
通过使用装饰器对象,可以避免不必要的继承以及子类的数量爆炸性增长问题。
缺点:
会导致系统变得复杂,增加了许多类和对象的相互关系,需要开发人员具备更高的抽象能力和设计能力。
增加了代码复杂度,使得项目开发和维护更加困难。
应用场景
生活场景
穿衣服:我们每天都要穿衣服,根据不同的场合和气温选择不同的服装,比如外套、围巾、帽子等,这些都是具体装饰器(Concrete Decorator),而我们就是被装饰的抽象组件(Component)。
手机配件:如手机壳、钢化膜等。都可以看作是装饰者模式的应用,手机就是抽象组件(Component),手机壳、钢化膜等都是具体装饰器(Concrete Decorator)。
电影特效:如烟雾、爆炸、光影等,影片为抽象组件(Component),各种特效就是具体装饰器(Concrete Decorator)。
java场景
IO流的处理:这是一个典型的装饰者模式的应用。InputSteam和OutputStream是最基本的抽象组件(Component),而各种FilterInputSteam和FilterOutputStream就是具体的装饰器,它们可以实现各种不同的IO流处理功能,如缓冲、压缩、加密等等。
数据库连接池:连接池为抽象组件(Component),各种不同的连接池实现(如C3P0、DBCP等)则是具体的装饰器(Concrete Decorator),它们可以实现不同的连接池缓存策略、连接池大小、超时时间等属性。
Spring的AOP:在AOP中,切面就是具体的装饰器(Concrete Decorator),而业务逻辑则是抽象组件(Component),通过动态代理技术,将具体的业务逻辑和切面对象组合起来,我们就可以实现在不修改源码的情况下,动态地为业务逻辑添加新的功能。
示例
下面以英雄联盟中李青(盲僧)学技能为例。其中Hero代表英雄(抽象构件)、BlindMonk(盲僧-具体构件)、SkillDecorator(技能-抽象装饰器)、QWERDecorator为英雄的四个技能(具体装饰器)
Hero(英雄-抽象组件)
/**
* 英雄
*/
public interface Hero {
/**
* 学技能
*/
void learnSkill();
}
BlindMonk(盲僧-具体构件)
import lombok.Data;
/**
* 盲僧
*/
@Data
public class BlindMonk implements Hero{
private String name;
public BlindMonk(String name){
this.name=name;
}
@Override
public void learnSkill() {
System.out.println(getName());
}
}
SkillDecorator(技能装饰-抽象装饰器)
/**
* 学技能(装饰hero)
*/
public interface SkillDecorator extends Hero{
void learnSkill();
}
QDecorator(Q技能-具体装饰器)
/**
* Q技能
*/
public class QDecorator implements SkillDecorator{
private Hero hero;
private String name;
public QDecorator(Hero hero,String name){
this.name=name;
this.hero=hero;
}
private void learnQ(){
System.out.println("习得技能"+name);
}
@Override
public void learnSkill() {
hero.learnSkill();
learnQ();
}
}
WDecorator(W技能-具体装饰器)
/**
* W技能
*/
public class WDecorator implements SkillDecorator{
private Hero hero;
private String name;
public WDecorator(Hero hero,String name){
this.name=name;
this.hero=hero;
}
private void learnW(){
System.out.println("习得技能"+name);
}
@Override
public void learnSkill() {
hero.learnSkill();
learnW();
}
}
EDecorator(E技能-具体装饰器)
/**
* E技能
*/
public class EDecorator implements SkillDecorator{
private Hero hero;
private String name;
public EDecorator(Hero hero,String name){
this.name=name;
this.hero=hero;
}
private void learnE(){
System.out.println("习得技能"+name);
}
@Override
public void learnSkill() {
hero.learnSkill();
learnE();
}
}
RDecorator(R技能-具体装饰器)
/**
* R技能
*/
public class RDecorator implements SkillDecorator{
private Hero hero;
private String name;
public RDecorator(Hero hero,String name){
this.name=name;
this.hero=hero;
}
private void learnR(){
System.out.println("习得技能"+name);
}
@Override
public void learnSkill() {
hero.learnSkill();
learnR();
}
}
测试类
/**
* 测试类
*/
@SpringBootTest
public class TestDecoration {
@Test
void testDecoration(){
QDecorator q=new QDecorator(new BlindMonk("李青"),"Q:天音波/回音击");
WDecorator w=new WDecorator(q,"W:金钟罩/铁布衫");
EDecorator e=new EDecorator(w,"E:天雷破/摧筋断骨");
RDecorator r=new RDecorator(e,"R:猛龙摆尾");
r.learnSkill();
}
}
总结
给对象添加一些职责,但是又不想改变其原有的接口和实现。
需要对不同组合的对象进行扩展,从而避免出现由于继承关系带来的类很多问题。
在不使用继承的情况下动态地为一个对象添加一些额外的功能。
需要在程序运行时动态地为对象添加不同的功能,或者为对象添加同时使用多个的功能。
一句话,当遇到需要给某个类添加新功能,但又不能改源代码或不希望影响其他对象的情况下,可以考虑装饰者模式。
- 感谢你赐予我前进的力量