装饰者模式,又叫做包装模式,指的是不改变原有对象的基础上,把功能附加在对象上。提供了比继承更加有弹性的扩展方案,属于结构型设计模式。
1,从一个例子开始
相信很多人都玩过《和平精英》这个游戏。在游戏中,玩家可以自由地捡一些枪械和部件,并把部件安装到枪械上,自由地组合一把枪以提升枪的功能,这其实就是装饰者模式的一个体现。
今天我们就来模拟一下这个例子。
首先创建一个枪械抽象类:
package com.gitee.swsk33.decorator.model;
import lombok.Getter;
import lombok.Setter;
/**
* 枪械
*/
@Getter
@Setter
public abstract class Gun {
/**
* 名字
*/
private String name;
/**
* 当前武器状态
*/
private String status;
/**
* 武器性能
*/
private int capability;
}
然后创建具体枪械类-98K步枪:
package com.gitee.swsk33.decorator.model.impl;
import com.gitee.swsk33.decorator.model.Gun;
/**
* 98K步枪
*/
public class Kar98K extends Gun {
/**
* 构造函数:初始化属性
*/
public Kar98K() {
setName("Kar98K");
setStatus("98K步枪");
setCapability(10);
}
}
好了,假设要给这个步枪加上八倍镜,那有的人可能会在这个类基础上又创建一个类Kar98KWithEightfoldScope并修改其参数,再加上一个子弹袋,就又创建一个类。。。
很显然这是不行的,在游戏中枪械和部件种类非常的多,而每一把枪和很多部件可以玩出多种组合,如果把每个组合都列举一遍,显然是非常不现实的。
这时,就要用到装饰者模式了。
2,创建装饰者抽象类和具体类
在这里,枪械部件都称之为装饰者,因为它们可以动态扩展枪械功能,枪械就是被装饰者。
创建装饰者的抽象:
package com.gitee.swsk33.decorator.wrapper;
import com.gitee.swsk33.decorator.model.Gun;
/**
* 枪的部件
*/
public abstract class GunWrapper extends Gun {
/**
* 传入要安装部件的枪械
*/
private Gun gun;
/**
* 构造器依赖注入
*
* @param gun 要安装部件的枪械
*/
public GunWrapper(Gun gun) {
this.gun = gun;
}
}
然后实现具体的装饰者。
八倍镜:
package com.gitee.swsk33.decorator.wrapper.impl;
import com.gitee.swsk33.decorator.model.Gun;
import com.gitee.swsk33.decorator.wrapper.GunWrapper;
/**
* 八倍镜部件
*/
public class EightfoldScope extends GunWrapper {
/**
* 构造器依赖注入
*
* @param gun 要安装部件的枪械
*/
public EightfoldScope(Gun gun) {
super(gun);
this.setStatus(gun.getStatus() + " + 八倍镜");
this.setCapability(gun.getCapability() + 5);
}
}
子弹袋:
package com.gitee.swsk33.decorator.wrapper.impl;
import com.gitee.swsk33.decorator.model.Gun;
import com.gitee.swsk33.decorator.wrapper.GunWrapper;
/**
* 子弹袋部件
*/
public class BulletBag extends GunWrapper {
/**
* 构造器依赖注入
*
* @param gun 要安装部件的枪械
*/
public BulletBag(Gun gun) {
super(gun);
this.setStatus(gun.getStatus() + " + 子弹袋");
this.setCapability(gun.getCapability() + 3);
}
}
这里可见:装饰者抽象类中有一个属性就是被装饰者,因为我们要先把被装饰者传入给装饰者实例,装饰者才能对被装饰者进行操作和附加。然后具体的装饰者,就会对传入的被装饰者进行一个功能的附加。
就像你要把具体的一个物品放进礼物盒子一样,礼物盒子就是装饰者。
我们来运行一下:
// 模拟玩家正在吃鸡游戏
// 捡到一把98K步枪
Gun kar98k = new Kar98K();
System.out.println("当前武器情况:" + kar98k.getStatus() + ",性能:" + kar98k.getCapability());
// 捡到一个八倍镜并安装至98K上面
kar98k = new EightfoldScope(kar98k);
System.out.println("当前武器情况:" + kar98k.getStatus() + ",性能:" + kar98k.getCapability());
// 捡到一个子弹袋并安装至98K上面
kar98k = new BulletBag(kar98k);
System.out.println("当前武器情况:" + kar98k.getStatus() + ",性能:" + kar98k.getCapability());
结果:
3,总结
可见装饰者模式,可以很灵活地对原有的对象进行扩展,我们需要什么装饰,就实例化什么装饰并附加给被装饰物,是非常的方便灵活的。
可见装饰者模式中,有以下类:
- 抽象被装饰者:上面的枪械抽象类
- 具体被装饰者:上面的98K类
- 抽象装饰者:上面的部件抽象类
- 具体装饰者:上面的八倍镜、子弹袋类
通常使用装饰者接收被装饰者实例,在装饰者中对被装饰者实例进行功能附加,就完成了装饰的步骤。上述例子比较简单,就在构造函数中对被装饰者进行了附加。很多时候在装饰者中会重写被装饰者的某个成员方法实现功能附加。
并且,具体被装饰者和抽象装饰者都有着共同的超类(父类):抽象被装饰者。这样使得装饰之后的实例,仍然属于最初的类型。
装饰者模式的类图如下: