装饰器模式属于结构型模式,主要解决当系统需要添加新功能,需要向旧类中不断添加新的属性和方法,从而导致整个类的复杂度不断增长的问题。假如新加的代码仅仅是为了满足特定场景下才会执行的需要,那么就没必要全部写在主类中,此时就可以使用装饰模式来解决,把要装饰的代码写在新加的类中,在需要的时候动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活。
文章目录
装饰器模式的介绍
装饰器(Decorator)模式也可叫包装模式(Wrapper Pattern) ,是为已有功能动态添加更多功能的一种方式。装饰器通过新建装饰类,用来包装原有的类,在保证原有类结构完整的情况下添加新功能。此模式将原有类当做组件,或者为原有类新建一个抽象类当做组件接口,原有类和装饰类通过继承该抽象类来实现关联。当需要使用装饰功能时只需要让装饰类包装原有类的对象即可完成装饰。
优点
- 装饰器是继承的有力补充,它比继承灵活,在不改变原有对象的情况下,能够动态的给一个对象扩展功能
- 有效的将类的核心功能和装饰功能分离,去除相关类中重复的装饰逻辑
- 装饰类和被装饰类解耦,可以独立修改扩展
- 通过使用不同装饰类与被装饰类组合,实现不同的功能实现
- 符合开闭原则
缺点
- 装饰类通过添加新的子类来实现新的功能,到后期可能会出现大量装饰子类,增加复杂性
- 多层装饰会导致程序层次更复杂
应用场景
-
当对象的功能要求可以动态地添加,也可以再动态地撤销时
-
当需要给一个现有类添加附加职责,而又不能采用生成子类的方法进行扩充时。例如,该类被隐藏或者该类是终极类或者采用继承方式会产生大量的子类
-
当需要通过对现有的一组基本功能进行排列组合而产生非常多的功能时,采用继承关系很难实现,而采用装饰器模式却很好实现
-
在Java IO中
OutputStream
就具备多个装饰子类,如ByteArrayOutputStream
、ObjectOutputStream
、FileOutputStream
等都是对输出流功能的扩展。
装饰器模式的使用
例子:可恶的怪物正在践踏我们的家园!万幸科学家们已经完成了对机甲(被装饰类)的研制工作,工程师们也为机甲设计了多套强大的扩展装甲(装饰类),是时候组装我们的最强机甲了,把那些怪物溺死在太平洋吧!
类图
装饰器中存在四个角色
-
抽象构件(Component)角色:该角色定义一个抽象接口以规范准备接收额外责任的对象,但我的例子中因为只有一个机甲,所以不需要设置该角色
-
具体构件(Concrete Component)角色:实现抽象构件,通过装饰该角色来为其添加一些额外的职责,对应上方的机甲类
-
抽象装饰(Decorator)角色:该角色继承抽象构件,为抽象类,类中持有具体构件的实例,以此达到通过其子类扩展具体构件职责的功能,对应上方外设类
-
具体装饰(Concrete Decorator)角色:实现抽象装饰角色的相关方法,以此给具体构件对象添加附加的责任
实现方法
第一步,编写具体构建角色(机甲类)
机甲类
package 设计模式.结构型模式.装饰器模式;
/**
* 具体构件角色,因为本案例只有一个具体构件,所以不需要设置抽象构件来约束构件角色
*/
public class 机甲类 {
public void 展示(){
System.out.println("机甲开始组装……组装完毕!");
}
}
第二步,编写抽象装饰角色(外设类)
外设类
package 设计模式.结构型模式.装饰器模式;
/**
* 抽象装饰角色,约束具体装饰类的方法
*/
public abstract class 外设类 extends 机甲类 {
protected 机甲类 待装机甲;
public 外设类(机甲类 待装机甲){
// 持有被装饰类,通过聚合关系达到被装饰类与装饰类的组合
this.待装机甲 = 待装机甲;
}
// 规定具体装饰类通过该方法来对被装饰类的功能进行扩展
public abstract void 展示();
}
第三步,编写具体装饰角色(钛合金装甲等模块)
钛合金装甲模块
package 设计模式.结构型模式.装饰器模式;
public class 钛合金装甲模块 extends 外设类 {
public 钛合金装甲模块(机甲类 待装机甲) {
super(待装机甲);
}
@Override
public void 展示() {
// 执行装饰类附加的功能
System.out.println("装备【钛合金装甲模块】");
// 最后执行被装饰类的功能
待装机甲.展示();
}
}
微型核能续航模块
package 设计模式.结构型模式.装饰器模式;
public class 微型核能续航模块 extends 外设类 {
public 微型核能续航模块(机甲类 待装机甲) {
super(待装机甲);
}
@Override
public void 展示() {
// 执行装饰类附加的功能
System.out.println("装备【微型核能续航模块】");
// 最后执行被装饰类的功能
待装机甲.展示();
}
}
自动追踪导弹模块
package 设计模式.结构型模式.装饰器模式;
public class 自动追踪导弹模块 extends 外设类 {
public 自动追踪导弹模块(机甲类 待装机甲) {
super(待装机甲);
}
@Override
public void 展示() {
// 执行装饰类附加的功能
System.out.println("装备【自动追踪导弹模块】");
// 最后执行被装饰类的功能
待装机甲.展示();
}
}
多功能组合臂模块
package 设计模式.结构型模式.装饰器模式;
public class 多功能组合臂模块 extends 外设类 {
public 多功能组合臂模块(机甲类 待装机甲) {
super(待装机甲);
}
@Override
public void 展示() {
// 执行装饰类附加的功能
System.out.println("装备【多功能组合臂模块】");
// 最后执行被装饰类的功能
待装机甲.展示();
}
}
重装弱重力推进器
package 设计模式.结构型模式.装饰器模式;
public class 重装弱重力推进器 extends 外设类 {
public 重装弱重力推进器(机甲类 待装机甲) {
super(待装机甲);
}
@Override
public void 展示() {
// 执行装饰类附加的功能
System.out.println("装备【重装弱重力推进器】");
// 最后执行被装饰类的功能
待装机甲.展示();
}
}
第四步,编写测试类测试
测试类
package 设计模式.结构型模式.装饰器模式;
public class 测试类 {
public static void main(String[] args) {
// 开始组装第一台机甲
System.out.println("开始组装第一台机甲:爱国机甲");
机甲类 爱国机甲 = new 机甲类();
// 对爱国机甲进行装饰
爱国机甲 = new 自动追踪导弹模块(爱国机甲);
爱国机甲 = new 微型核能续航模块(爱国机甲);
爱国机甲 = new 钛合金装甲模块(爱国机甲);
// 展示爱国机甲
爱国机甲.展示();
System.out.println("——————————————————————————————————");
// 组装第二台机甲,对被装饰类进行不同的装饰
System.out.println("开始组装第二台机甲:友善机甲");
机甲类 友善机甲 = new 机甲类();
// 对友善机甲进行装饰
友善机甲 = new 重装弱重力推进器(友善机甲);
友善机甲 = new 钛合金装甲模块(友善机甲);
友善机甲 = new 多功能组合臂模块(友善机甲);
友善机甲 = new 微型核能续航模块(友善机甲);
友善机甲 = new 自动追踪导弹模块(友善机甲);
// 展示友善机甲
友善机甲.展示();
}
}
测试结果
开始组装第一台机甲:爱国机甲
装备【钛合金装甲模块】
装备【微型核能续航模块】
装备【自动追踪导弹模块】
机甲开始组装……组装完毕!
——————————————————————————————————
开始组装第二台机甲:友善机甲
装备【自动追踪导弹模块】
装备【微型核能续航模块】
装备【多功能组合臂模块】
装备【钛合金装甲模块】
装备【重装弱重力推进器】
机甲开始组装……组装完毕!
Process finished with exit code 0
本文参考
《大话设置模式》 作者:程杰