装饰模式是设计模式中用得比较频繁的模式了,他是一种不需要改变原始类代码实现功能拓展的一种成熟模式。
那他用在什么场景呢,项目中我们常常遇到这样一种情况,比如某个类的执行某个方法返回了类中的一个变量值,现在有个需求就是变量值我们需要随时改,你首先想到的是修改里面的参数,那如果这个参数是private类型呢,你想那也行啊,我设置一个set方法,那如果这个类没有set方法呢,外界不应许修改或者他本身不是变量是常量怎么办?那你可能会想到另外一个办法,我可以通过反射来修改对象中的属性啊,没错,是可以!但是反射的一个很大缺点就是慢,不适用于过多使用,从系统的健壮性考虑,这个方案不好,那么我们这个时候用装饰模式就能生效了。
实战场景:相信大家都玩过dota、lol这样的游戏,喜欢玩DPS的英雄都希望攻击越高越好,通过打钱然后买装备,英雄有一个初始攻击力(50),假如我通过带装备增加了攻击力,那我们实现起来总不能每带一件装备就修改对象的参数一次吧,而且直接修改角色的属性势必会增加程序的耦合性。那么用装饰模式如何做到呢。不多说 先看类图:
装饰模式里有四种角色,
1.抽象组件:抽象类,定义了一个装饰类需要被装饰的方法,就是上面的角色类,我们这里可以看做是带装备的英雄。
2.具体组件:抽象组件的子类,即被装饰着。对于的就是我们类图的英雄,你可以看做是一个裸装的英雄
3.装饰类:也是抽象类的一个子类,但“”装饰类"也包含抽象组件声明的变量,以保存“”被装饰着"的引用。
4.具体装饰类:听名字都知道是“”装饰者“,就是圣剑、金箍棒这样的武器装备
刚看代码可能还不能理解透彻,下面用代码来加以说明:
抽象组件:
/**
* desc 抽象组件 扮演英雄的角色(带装备的)
*/
public interface Role {
public int attact();
}
具体组件:
/**
* desc 具体组件,英雄类(初始属性,不带装备的)
*/
public class Hero implements Role{
private final int DPS = 50;
@Override
public int attact() {
return DPS;
}
}
装饰类:
/**
* 装饰类(抽象的)
*/
public abstract class Equipment implements Role{
protected Role role;
public Equipment(){
}
public Equipment(Role role){
this.role = role;
}
}
具体三个装饰类:
/**
* desc 阔剑装饰类(攻击力加15)
*/
public class Glaive extends Equipment{
private final int DPS = 15;
public Glaive(Role role){
super(role);
}
@Override
public int attact() {
return role.attact() + DPS;
}
}
/**
* desc 金箍棒装饰类(攻击力加150)
*/
public class GoldenCudgel extends Equipment{
private final int DPS = 150;
public GoldenCudgel(Role role){
super(role);
}
@Override
public int attact() {
return role.attact() + DPS;
}
/**
* desc 圣剑装饰类(攻击力加300)
*/
public class Excalibur extends Equipment{
private final int DPS = 300;
public Excalibur(Role role){
super(role);
}
@Override
public int attact() {
return role.attact() + DPS;
}
}
下面是测试类:
public class Main {
public void showDPS(Role role){
System.out.println("孙悟空的此时的输出是"+role.attact());
}
public static void main(String args[]){
Main main = new Main();
Role role = new Hero(); //实例化这个英雄
main.showDPS(role); //打印出50的攻击力
Role role1 = new Glaive(role); //给裸装英雄添加一个阔剑
main.showDPS(role1); //打印出65的攻击力
Role role2 = new GoldenCudgel(role1); //给有阔剑的英雄添加一个金箍棒
main.showDPS(role2); //打印出215的攻击力
Role role3= new Excalibur(role1); //给有阔剑的英雄添加一个圣剑
main.showDPS(role3); //打印出365的攻击力
}
}
打印结果:
怎么样,是不是很简单,装饰模式在实际当中运用的挺广泛的,装饰模式满足“开闭原则”,不必修改具体组件就可以增加新的针对该组件的具体装饰。被装饰着和装饰着是送耦合的关系。
是靠一下,如果我有一个需求,除了随时添加装备,那我还要随时脱掉装备怎么办呢,很简单!从面向对象的思维来想,装备肯定不会自己把自己脱掉,肯定是角色(Role)啦!在Role这个抽象组件中再添加一个卸装备的动作,然后每个具体装饰类实现卸掉的逻辑不就可以了吗?