11,装饰模式-孙悟空的六神装

207 阅读12分钟

一,前言

7种结构型设计模式:桥接模式,适配器模式,装饰模式,组合模式,享元模式,外观模式,代理模式

上篇我们说了外观模式:提供一个统一接口来访问子系统中的一群接口
外观定义了一个高层接口,让子系统更容易使用

开发中我们为一个对象添加新行为通常会采用继承的方式
但通常这样做会导致子类泛滥的结果也就是我们常说的类爆炸,为后续的维护带来不便
本着"开放-关闭"的设计原则,我们希望在不修改原有代码的情况下,为对象赋予新的职责

这篇的结构型设计模式就可以做到,它就是装饰模式,也叫做装饰器或装饰者模式

二,装饰模式简介

1,简介:

装饰者模式:动态地将责任附加到对象上,在不修改任何底层代码的情况下,为对象赋予新的职责

2,装饰模式类图:

装饰者模式类图

从类图中我们可以看到:
    装饰者和被装饰者(组件)拥有相同的接口,所以装饰者能够取代被装饰者
    装饰者与组件进行组合时,通过对象组合为组件加入新的行为
    所以装饰者模式能在不创建更多子类的情况下,实现对象功能的拓展

    这也正是组合的威力所在,多用组合少用继承
    如果依赖继承,类的行为只能在编译时静态决定,行为来自超类或子类进行覆盖
    组合可以把装饰器和组件进行混合,而且是在运行时才决定具体行为

3,装饰模式中的角色:

抽象组件(Component)角色:给出一个抽象接口,规范准备接收附加责任的对象。
具体组件(ConcreteComponent)角色:定义一个将要接收附加责任的类。

装饰(Decorator)角色:持有一个组件对象(Component)的实例,并定义一个与抽象构件接口一致的接口。
具体装饰(ConcreteDecorator)角色:负责为组件对象赋予新职责。

三,装饰模式场景

我们选取一个比较适合装饰模式的场景来对这个模式进行学习
最后我选择了游戏中为角色添加装备来获得属性加成的例子
那么这篇的主题又回到了大家感兴趣的游戏话题上
我们就来看看"王者荣耀"中孙悟空这个英雄的六神装
ps:这里对英雄和装备属性做了修改,和真实游戏中的属性不一致,望谅解!

孙悟空

属性

我们的场景是这样的:
    1,创建一个抽象组件Hero(即抽象组件角色),通过继承此类创建具体英雄,此类定义了英雄属性
    2,通过继承Hero类创建具体英雄(孙悟空),并针对英雄个性化做属性调整
    3,创建一个抽象装备类Equipment(即抽象装饰角色),继承自组件Hero,通过继承此类创建具体装备,此类需定义装饰新功能的接口,以重写为新的功能供外部调用
    4,通过继承Equipment类创建具体装备类,此类需持有一个具体英雄(Hero)的引用,重写接口时对此引用信息做修改,以实现功能的变更和添加
    5,使用4的方式创建6件装备,并逐一为英雄装饰,打印日志查看装饰后的英雄属性

四,装饰模式例子

1,创建抽象组件Hero,即抽象组件角色,定义英雄属性

package com.brave.decorator.pesticide.hero;

/**
 * 抽象英雄
 *  -每个具体英雄都继承自此类
 *  -默认属性数值均为100暴击率0%暴击效果2倍
 * 
 * @author Brave
 *
 */
public abstract class Hero {

    private String name = "";   // 名称
    private double HP = 100;    // 生命值
    private double MP = 100;    // 魔法值
    private double ATK = 100;   // 攻击力
    private double DEF = 100;   // 防御力
    private double CRT = 0;     // 暴击率加成
    private double CRTE = 2;    // 暴击效果
    private double MS = 0;      // 移动速度加成
    private double AB = 0;      // 破甲加成
    private String equipment = "";  // 装备

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getHP() {
        return HP;
    }

    public void setHP(double hP) {
        HP = hP;
    }

    public double getMP() {
        return MP;
    }

    public void setMP(double mP) {
        MP = mP;
    }

    public double getATK() {
        return ATK;
    }

    public void setATK(double aTK) {
        ATK = aTK;
    }

    public double getDEF() {
        return DEF;
    }

    public void setDEF(double dEF) {
        DEF = dEF;
    }

    public double getCRT() {
        return CRT;
    }

    public void setCRT(double cRT) {
        CRT = cRT;
    }

    public double getCRTE() {
        return CRTE;
    }

    public void setCRTE(double cRTE) {
        CRTE = cRTE;
    }

    public double getMS() {
        return MS;
    }

    public void setMS(double mS) {
        MS = mS;
    }

    public String getEquipment() {
        return equipment;
    }

    public void setEquipment(String equipment) {
        this.equipment = equipment;
    }

    public double getAB() {
        return AB;
    }

    public void setAB(double aB) {
        AB = aB;
    }

    public void printHeroAttr() {
        System.out.println("name = " + name);
        System.out.println("生命值:HP = " + HP);
        System.out.println("魔法值:MP = " + MP);
        System.out.println("攻击力:ATK = " + ATK);
        System.out.println("防御力:DEF = " + DEF);
        System.out.println("暴击率加成:CRT = " + CRT);
        System.out.println("暴击效果:CRTE = " + CRTE);
        System.out.println("移动速度加成:MS = " + MS);
        if(!equipment.equals(""))System.out.println("装备:equipment = " + equipment);
    }

}

2,通过继承Hero类创建具体英雄孙悟空,针对英雄个性化做属性调整

package com.brave.decorator.pesticide.hero;

/**
 * 具体英雄-孙悟空
 * 初始暴击率为20%,暴击伤害150%;
 * 
 * @author Brave
 *
 */
public class MonkeyKing extends Hero {

    public MonkeyKing() {
        setName("孙悟空");
        setCRT(0.2);
        setCRTE(1.5);
    }

}

3,继承自组件Hero,创建抽象装备类Equipment,定义装饰新功能的接口,以重写为新的功能供外部调用

package com.brave.decorator.pesticide.equipment;

import com.brave.decorator.pesticide.hero.Hero;

/**
 * 抽象装备
 *  -每个具体装备都继承自此类
 * 
 * @author Brave
 *
 */
public abstract class Equipment extends Hero{

    // 强制重写-装备名称
    public abstract String getName();

    // 输出准备此件装备后的所有装备信息
    public abstract double getHP();
    public abstract double getMP();
    public abstract double getATK();
    public abstract double getDEF();
    public abstract double getCRT();
    public abstract double getCRTE();
    public abstract double getMS();
    public abstract double getAB();
    public abstract String getEquipment();

    // 强制重写-输出装备此件装备后的全部属性
    public abstract void printHeroAttr();

}

4,通过继承Equipment类创建具体装备类
此类持有一个具体英雄(Hero)的引用,重写接口时对此引用信息做修改,以实现功能的变更和添加

创建6件装备:
    鞋子:100移速
    无尽之刃:50%暴击+50%暴击效果
    破甲弓:100攻击+100破甲
    冰痕之握:100防御+100魔法
    装饰泣血之刃:100攻击+100生命
    破军:200攻击

1)鞋子:增加100点移动速度

package com.brave.decorator.pesticide.equipment;

import com.brave.decorator.pesticide.hero.Hero;

/**
 * 鞋子:增加100点移动速度
 * @author Brave
 *
 */
public class Shoes extends Equipment {

    private Hero hero;

    public Shoes(Hero hero) {
        this.hero = hero;
    }

    @Override
    public void printHeroAttr() {
        System.out.println("name = " + getName());
        System.out.println("生命值:HP = " + getHP());
        System.out.println("魔法值:MP = " + getMP());
        System.out.println("攻击力:ATK = " + getATK());
        System.out.println("防御力:DEF = " + getDEF());
        System.out.println("暴击率加成:CRT = " + getCRT());
        System.out.println("暴击效果:CRTE = " + getCRTE());
        System.out.println("移动速度加成:MS = " + getMS());
        System.out.println("破甲加成:AB = " + getAB());
        System.out.println(getEquipment().equals("") ? "" : "装备:equipment = " + getEquipment());
    }

    @Override
    public String getName() {
        return "鞋子";
    }

    @Override
    public double getHP() {
        return hero.getHP();
    }

    @Override
    public double getMP() {
        return hero.getMP();
    }

    @Override
    public double getATK() {
        return hero.getATK();
    }

    @Override
    public double getDEF() {
        return hero.getDEF();
    }

    @Override
    public double getCRT() {
        return hero.getCRT();
    }

    @Override
    public double getCRTE() {
        return hero.getCRTE();
    }

    @Override
    public double getMS() {
        return hero.getMS() + 100;
    }

    @Override
    public String getEquipment() {
        return hero.getEquipment() + getName() + ",";
    }

    @Override
    public double getAB() {
        return hero.getAB();
    }

}

2)无尽之刃:50%暴击+50%暴击效果

package com.brave.decorator.pesticide.equipment;

import com.brave.decorator.pesticide.hero.Hero;

/**
 * 无尽之刃:增加暴击率50%.暴击效果50%
 * @author Brave
 *
 */
public class InfinitySword extends Equipment {

    private Hero hero;

    public InfinitySword(Hero hero) {
        this.hero = hero;
    }

    @Override
    public void printHeroAttr() {
        System.out.println("name = " + getName());
        System.out.println("生命值:HP = " + getHP());
        System.out.println("魔法值:MP = " + getMP());
        System.out.println("攻击力:ATK = " + getATK());
        System.out.println("防御力:DEF = " + getDEF());
        System.out.println("暴击率加成:CRT = " + getCRT());
        System.out.println("暴击效果:CRTE = " + getCRTE());
        System.out.println("移动速度加成:MS = " + getMS());
        System.out.println("破甲加成:AB = " + getAB());
        System.out.println(getEquipment().equals("") ? "" : "装备:equipment = " + getEquipment());
    }

    @Override
    public String getName() {
        return "无尽之刃";
    }

    @Override
    public double getHP() {
        return hero.getHP();
    }

    @Override
    public double getMP() {
        return hero.getMP();
    }

    @Override
    public double getATK() {
        return hero.getATK();
    }

    @Override
    public double getDEF() {
        return hero.getDEF();
    }

    @Override
    public double getCRT() {
        return hero.getCRT() + 0.5;
    }

    @Override
    public double getCRTE() {
        return hero.getCRTE() + 0.5;
    }

    @Override
    public double getMS() {
        return hero.getMS();
    }

    @Override
    public String getEquipment() {
        return hero.getEquipment() + getName() + ",";
    }

    @Override
    public double getAB() {
        return hero.getAB();
    }

}

3)破甲弓:100攻击+100破甲

package com.brave.decorator.pesticide.equipment;

import com.brave.decorator.pesticide.hero.Hero;

/**
 * 破甲弓:100攻击+100破甲
 * @author Brave
 *
 */
public class ArmorBow extends Equipment {

    private Hero hero;

    public ArmorBow(Hero hero) {
        this.hero = hero;
    }

    @Override
    public void printHeroAttr() {
        System.out.println("name = " + getName());
        System.out.println("生命值:HP = " + getHP());
        System.out.println("魔法值:MP = " + getMP());
        System.out.println("攻击力:ATK = " + getATK());
        System.out.println("防御力:DEF = " + getDEF());
        System.out.println("暴击率加成:CRT = " + getCRT());
        System.out.println("暴击效果:CRTE = " + getCRTE());
        System.out.println("移动速度加成:MS = " + getMS());
        System.out.println("破甲加成:AB = " + getAB());
        System.out.println(getEquipment().equals("") ? "" : "装备:equipment = " + getEquipment());
    }

    @Override
    public String getName() {
        return "破甲弓";
    }

    @Override
    public double getHP() {
        return hero.getHP();
    }

    @Override
    public double getMP() {
        return hero.getMP();
    }

    @Override
    public double getATK() {
        return hero.getATK() + 100;
    }

    @Override
    public double getDEF() {
        return hero.getDEF();
    }

    @Override
    public double getCRT() {
        return hero.getCRT();
    }

    @Override
    public double getCRTE() {
        return hero.getCRTE();
    }

    @Override
    public double getMS() {
        return hero.getMS();
    }

    @Override
    public String getEquipment() {
        return hero.getEquipment() + getName() + ",";
    }

    @Override
    public double getAB() {
        return hero.getAB() + 100;
    }

}

4)冰痕之握:100防御+100魔法

package com.brave.decorator.pesticide.equipment;

import com.brave.decorator.pesticide.hero.Hero;

/**
 * 复活甲:100防御+100魔法值
 * @author Brave
 *
 */
public class IceMarkGrip extends Equipment {

    private Hero hero;

    public IceMarkGrip(Hero hero) {
        this.hero = hero;
    }

    @Override
    public void printHeroAttr() {
        System.out.println("name = " + getName());
        System.out.println("生命值:HP = " + getHP());
        System.out.println("魔法值:MP = " + getMP());
        System.out.println("攻击力:ATK = " + getATK());
        System.out.println("防御力:DEF = " + getDEF());
        System.out.println("暴击率加成:CRT = " + getCRT());
        System.out.println("暴击效果:CRTE = " + getCRTE());
        System.out.println("移动速度加成:MS = " + getMS());
        System.out.println("破甲加成:AB = " + getAB());
        System.out.println(getEquipment().equals("") ? "" : "装备:equipment = " + getEquipment());
    }

    @Override
    public String getName() {
        return "冰痕之握";
    }

    @Override
    public double getHP() {
        return hero.getHP();
    }

    @Override
    public double getMP() {
        return hero.getMP() + 100;
    }

    @Override
    public double getATK() {
        return hero.getATK();
    }

    @Override
    public double getDEF() {
        return hero.getDEF() + 100;
    }

    @Override
    public double getCRT() {
        return hero.getCRT();
    }

    @Override
    public double getCRTE() {
        return hero.getCRTE();
    }

    @Override
    public double getMS() {
        return hero.getMS();
    }

    @Override
    public String getEquipment() {
        return hero.getEquipment() + getName() + ",";
    }

    @Override
    public double getAB() {
        return hero.getAB();
    }

}

5)泣血之刃:100攻击+100生命

package com.brave.decorator.pesticide.equipment;

import com.brave.decorator.pesticide.hero.Hero;

/**
 * 泣血之刃:100攻击+100生命值
 * @author Brave
 *
 */
public class BloodSword extends Equipment {

    private Hero hero;

    public BloodSword(Hero hero) {
        this.hero = hero;
    }

    @Override
    public void printHeroAttr() {
        System.out.println("name = " + getName());
        System.out.println("生命值:HP = " + getHP());
        System.out.println("魔法值:MP = " + getMP());
        System.out.println("攻击力:ATK = " + getATK());
        System.out.println("防御力:DEF = " + getDEF());
        System.out.println("暴击率加成:CRT = " + getCRT());
        System.out.println("暴击效果:CRTE = " + getCRTE());
        System.out.println("移动速度加成:MS = " + getMS());
        System.out.println("破甲加成:AB = " + getAB());
        System.out.println(getEquipment().equals("") ? "" : "装备:equipment = " + getEquipment());
    }

    @Override
    public String getName() {
        return "泣血之刃";
    }

    @Override
    public double getHP() {
        return hero.getHP() + 100;
    }

    @Override
    public double getMP() {
        return hero.getMP();
    }

    @Override
    public double getATK() {
        return hero.getATK() + 100;
    }

    @Override
    public double getDEF() {
        return hero.getDEF();
    }

    @Override
    public double getCRT() {
        return hero.getCRT();
    }

    @Override
    public double getCRTE() {
        return hero.getCRTE();
    }

    @Override
    public double getMS() {
        return hero.getMS();
    }

    @Override
    public String getEquipment() {
        return hero.getEquipment() + getName() + ",";
    }

    @Override
    public double getAB() {
        return hero.getAB();
    }

}

6)破军:200攻击

package com.brave.decorator.pesticide.equipment;

import com.brave.decorator.pesticide.hero.Hero;

/**
 * 破军:200攻击
 * @author Brave
 *
 */
public class Breach extends Equipment {

    private Hero hero;

    public Breach(Hero hero) {
        this.hero = hero;
    }

    @Override
    public void printHeroAttr() {
        System.out.println("name = " + getName());
        System.out.println("生命值:HP = " + getHP());
        System.out.println("魔法值:MP = " + getMP());
        System.out.println("攻击力:ATK = " + getATK());
        System.out.println("防御力:DEF = " + getDEF());
        System.out.println("暴击率加成:CRT = " + getCRT());
        System.out.println("暴击效果:CRTE = " + getCRTE());
        System.out.println("移动速度加成:MS = " + getMS());
        System.out.println("破甲加成:AB = " + getAB());
        System.out.println(getEquipment().equals("") ? "" : "装备:equipment = " + getEquipment());
    }

    @Override
    public String getName() {
        return "破甲弓";
    }

    @Override
    public double getHP() {
        return hero.getHP();
    }

    @Override
    public double getMP() {
        return hero.getMP();
    }

    @Override
    public double getATK() {
        return hero.getATK() + 200;
    }

    @Override
    public double getDEF() {
        return hero.getDEF();
    }

    @Override
    public double getCRT() {
        return hero.getCRT();
    }

    @Override
    public double getCRTE() {
        return hero.getCRTE();
    }

    @Override
    public double getMS() {
        return hero.getMS();
    }

    @Override
    public String getEquipment() {
        return hero.getEquipment() + getName() + ",";
    }

    @Override
    public double getAB() {
        return hero.getAB();
    }

}

5,测试装饰模式,逐一为英雄装饰6件装备,打印日志查看装饰后的英雄属性

package com.brave.decorator.pesticide;

import com.brave.decorator.pesticide.equipment.ArmorBow;
import com.brave.decorator.pesticide.equipment.BloodSword;
import com.brave.decorator.pesticide.equipment.Breach;
import com.brave.decorator.pesticide.equipment.IceMarkGrip;
import com.brave.decorator.pesticide.equipment.InfinitySword;
import com.brave.decorator.pesticide.equipment.Shoes;
import com.brave.decorator.pesticide.hero.Hero;
import com.brave.decorator.pesticide.hero.MonkeyKing;

/**
 * 测试装饰模式
 * 
 * 在不修改任何底层代码的情况下.为对象赋予新的职责
 * 装饰者模式:动态地将责任附加到对象上,若要拓展功能,装饰者提供了比继承更有弹性的替代方案
 * 
 * @author Brave
 *
 */
public class Client {

    public static void main(String[] args) {

        System.out.println("*****************初始化英雄******************");
        Hero hero = new MonkeyKing();
        hero.printHeroAttr();

        System.out.println("*****************装饰鞋子:100移速******************");
        Hero shoes = new Shoes(hero);
        shoes.printHeroAttr();
        System.out.println("*****************装饰无尽之刃:50%暴击+50%暴击效果******************");
        Hero infinitySword = new InfinitySword(shoes);
        infinitySword.printHeroAttr();
        System.out.println("*****************装饰破甲弓:100攻击+100破甲******************");
        Hero armorBow = new ArmorBow(infinitySword);
        armorBow.printHeroAttr();
        System.out.println("*****************装饰冰痕之握:100防御+100魔法******************");
        Hero iceMarkGrip = new IceMarkGrip(armorBow);
        iceMarkGrip.printHeroAttr();
        System.out.println("*****************装饰泣血之刃:100攻击+100生命******************");
        Hero bloodSword = new BloodSword(iceMarkGrip);
        bloodSword.printHeroAttr();
        System.out.println("*****************装饰破军:200攻击******************");
        Hero breach = new Breach(bloodSword);
        breach.printHeroAttr();
    }

}

6,打印日志输出,查看属性

*****************初始化英雄******************
name = 孙悟空
生命值:HP = 100.0
魔法值:MP = 100.0
攻击力:ATK = 100.0
防御力:DEF = 100.0
暴击率加成:CRT = 0.2
暴击效果:CRTE = 1.5
移动速度加成:MS = 0.0
*****************装饰鞋子:100移速******************
name = 鞋子
生命值:HP = 100.0
魔法值:MP = 100.0
攻击力:ATK = 100.0
防御力:DEF = 100.0
暴击率加成:CRT = 0.2
暴击效果:CRTE = 1.5
移动速度加成:MS = 100.0
破甲加成:AB = 0.0
装备:equipment = 鞋子,
*****************装饰无尽之刃:50%暴击+50%暴击效果******************
name = 无尽之刃
生命值:HP = 100.0
魔法值:MP = 100.0
攻击力:ATK = 100.0
防御力:DEF = 100.0
暴击率加成:CRT = 0.7
暴击效果:CRTE = 2.0
移动速度加成:MS = 100.0
破甲加成:AB = 0.0
装备:equipment = 鞋子,无尽之刃,
*****************装饰破甲弓:100攻击+100破甲******************
name = 破甲弓
生命值:HP = 100.0
魔法值:MP = 100.0
攻击力:ATK = 200.0
防御力:DEF = 100.0
暴击率加成:CRT = 0.7
暴击效果:CRTE = 2.0
移动速度加成:MS = 100.0
破甲加成:AB = 100.0
装备:equipment = 鞋子,无尽之刃,破甲弓,
*****************装饰冰痕之握:100防御+100魔法******************
name = 冰痕之握
生命值:HP = 100.0
魔法值:MP = 200.0
攻击力:ATK = 200.0
防御力:DEF = 200.0
暴击率加成:CRT = 0.7
暴击效果:CRTE = 2.0
移动速度加成:MS = 100.0
破甲加成:AB = 100.0
装备:equipment = 鞋子,无尽之刃,破甲弓,冰痕之握,
*****************装饰泣血之刃:100攻击+100生命******************
name = 泣血之刃
生命值:HP = 200.0
魔法值:MP = 200.0
攻击力:ATK = 300.0
防御力:DEF = 200.0
暴击率加成:CRT = 0.7
暴击效果:CRTE = 2.0
移动速度加成:MS = 100.0
破甲加成:AB = 100.0
装备:equipment = 鞋子,无尽之刃,破甲弓,冰痕之握,泣血之刃,
*****************装饰破军:200攻击******************
name = 破甲弓
生命值:HP = 200.0
魔法值:MP = 200.0
攻击力:ATK = 500.0
防御力:DEF = 200.0
暴击率加成:CRT = 0.7
暴击效果:CRTE = 2.0
移动速度加成:MS = 100.0
破甲加成:AB = 100.0
装备:equipment = 鞋子,无尽之刃,破甲弓,冰痕之握,泣血之刃,破甲弓,

四,装饰模式的优点与缺点

装饰模式的优点:
1,装饰模式与继承的目的都是扩展对象功能,但装饰模式比继承更加灵活性。
  装饰模式允许系统动态决定添加装饰,或移除装饰
  继承关系是静态的,在系统运行前决定
2,可以灵活的"改造"对象,使用不同装饰器或改变排列组合顺序,实现多种组合

装饰模式的缺点:
  装饰模式使用组合实现新功能的添加,相比继承的实现使用了较少的类
  但由于装饰模式的特性,会产生比使用继承关系更多的对象
  而这些对象都很相似,数量也较多,所以加大了系统错误的排查难度

模式到这里就简单的说完了,先备注一些后面要加入的部分
java中使用的装饰模式-IO
装饰模式和其他模式的区别,例如和适配器