设计模式-Decorator模式

Decorator(装饰器)模式属于结构型模式。
比如当其需要三种不同的附加特性,可以为其创建三个派生类。但是若它还需要同时具有其中两种特性或者是各种特性的任意组合的时候,类继承的方法就不再适合了。
它允许向一个现有的对象不通过继承来添加新的功能,同时又不改变其结构。

一个例子(贪玩蓝月)

前一阵子张家辉代言的《贪玩蓝月》广告火了,“我系喳喳辉,是兄弟就来砍我~”被洗脑到现在,正好用这个游戏来解释一下装饰器模式。

玩游戏的人都知道这种类传奇的游戏核心玩法就是买装备,打怪,升级,买装备这样反复。

刚注册账号进入游戏的玩家假设只有一条大裤衩,价值5金币,随着刷怪升级,身上的装备也在一件件增多,这时候我们需要知道身上的装备价值多少金币。

定义玩家

public interface Gamer {
    /**
     * 获取目前的装备
     * @return
     */
    String getEquip();

    /**
     * 获取目前身上装备的价格
     * @return
     */
    int getPrice();
}

定义具体的法师职业玩家

public class MasterGamer implements Gamer {
    /**
     * 获取目前的装备
     *
     * @return
     */
    @Override
    public String getEquip() {
        return "大裤衩";
    }

    /**
     * 获取目前身上装备的价格
     *
     * @return
     */
    @Override
    public int getPrice() {
        return 5;
    }
}

新法师玩家出门只有大裤衩,装备全靠打。

传统继承实现

装备“法师权杖”

public class TruncheonMasterGamer extends MasterGamer{
    /**
     * 获取目前的装备
     *
     * @return
     */
    @Override
    public String getEquip() {
        return super.getEquip()+",法师权杖";
    }

    /**
     * 获取目前身上装备的价格
     *
     * @return
     */
    @Override
    public int getPrice() {
        return super.getPrice()+50;
    }
}

继续装备“魔法斗篷”

public class CloakTruncheonMasterGamer extends TruncheonMasterGamer{
    /**
     * 获取目前的装备
     *
     * @return
     */
    @Override
    public String getEquip() {
        return super.getEquip()+",魔法斗篷";
    }

    /**
     * 获取目前身上装备的价格
     *
     * @return
     */
    @Override
    public int getPrice() {
        return super.getPrice()+80;
    }
}

注意,这里是在之前已经装备了“法师权杖”之上去继承。

计算装备价格

CloakTruncheonMasterGamer gamer = new CloakTruncheonMasterGamer();
System.out.println("当前装备:"+gamer.getEquip()+"\n装备总价值:"+gamer.getPrice());

输出结果

当前装备:大裤衩,法师权杖,魔法斗篷
装备总价值:135

装饰器模式实现

image
声明通用装饰器基类“装备”

public abstract class Equip implements Gamer {
    private Gamer gamer;

    public Equip(Gamer gamer) {
        this.gamer = gamer;
    }
    /**
     * 获取目前的装备
     *
     * @return
     */
    @Override
    public String getEquip() {
        return gamer.getEquip();
    }

    /**
     * 获取目前身上装备的价格
     *
     * @return
     */
    @Override
    public int getPrice() {
        return gamer.getPrice();
    }
}

具体装饰器“法师权杖”

public class Truncheon extends Equip {
    public Truncheon(Gamer gamer) {
        super(gamer);
    }

    /**
     * 获取目前的装备
     *
     * @return
     */
    @Override
    public String getEquip() {
        return super.getEquip()+",法师权杖";
    }

    /**
     * 获取目前身上装备的价格
     *
     * @return
     */
    @Override
    public int getPrice() {
        return super.getPrice()+50;
    }
}

具体装饰器“魔法斗篷”

public class Cloak extends Equip {
    public Cloak(Gamer gamer) {
        super(gamer);
    }

    /**
     * 获取目前的装备
     *
     * @return
     */
    @Override
    public String getEquip() {
        return super.getEquip()+",魔法斗篷";
    }

    /**
     * 获取目前身上装备的价格
     *
     * @return
     */
    @Override
    public int getPrice() {
        return super.getPrice()+80;
    }
}

计算装备价格

//创建一个法师玩家
Gamer gamer = new MasterGamer();
//给法师玩家装备法师权杖
gamer = new Truncheon(gamer);
//给法师玩家装备魔法斗篷
gamer = new Cloak(gamer);
System.out.println("当前装备:"+gamer.getEquip()+"\n装备总价值:"+gamer.getPrice());

输出结果

当前装备:大裤衩,法师权杖,魔法斗篷
装备总价值:135

对比

上面例子比较简单,传统继承实现和装饰器模式实现区别不是很明显,但仔细思考还是会发现一些区别:

  • 传统继承实现不自由,没有“组件化”特性。玩家的装备是可以随意组合,随意拆卸的,而这种特性对于继承来说只能通过各种各样的子类组合来实现。就像上面的例子,装备“法师权杖”和“魔法斗篷”需要在拥有“法师权杖”的基础上再去继承。
  • 装饰器模式实现,使得附属属性和主体分开,而又不单独存在(Equip类里面声明了Gamer对象)。装备和玩家是分开的,可以给玩家单独装备任何装备,也可以随意卸下装备。

总结

这种设计模式下不仅可以扩展一个类的功能,也可以动态增加功能,动态撤销。但缺点就是多层装饰使用起来相对比较复杂。本质是将具体功能职责划分(例如区分核心组件以及附加属性职责)减少子类直接继承父类的耦合性。


你可以在这里获取相关代码:设计模式-Decorator模式

posted @ 2019-09-05 17:58  墨、鱼的blog  阅读(785)  评论(0编辑  收藏  举报