Java设计模式之从[魔兽世界包裹系统]分析组合(Composite)模式

  RPG游戏中的包裹(或者称为背包)是玩家携带物品的地方,它的大小决定着玩家能够携带物品数量。如在魔兽世界中,玩家起初的物品栏(将物品栏视为玩家的唯一一个包裹)的格子很少,但是玩家可以将新的包裹放在物品栏中,达到扩充物品栏的效果。也就是说,物品栏可以放消耗品、武器等零散的物品,当然也可以放包裹。

  假定魔兽世界有如下设定:玩家一开始拥有一个默认的包裹(物品栏),玩家可以获得新的包裹放在物品栏上。包裹可以存放武器、防具、消耗品,以及包裹。它可以无限次的迭代,形成树状,如下所示:

Java设计模式之从[魔兽世界包裹系统]分析组合(Composite)模式

  以上的模型反应了组合(Composite)模式的思维。在上述的树中,我们把节点分为两种:部分和整体。所谓“部分”,就是指那些没有子节点的项,如炉石、匕首等;整体是指拥有子节点的项,它可以储存子节点。组合模式的意图是将对象组成树形结构以表示“部分-整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性。

  下面是实现这个机制的Java代码:

import java.util.ArrayList;

interface WowItem {
    String getName();
    void add(WowItem item);
    void remove(WowItem item);
    WowItem get(int i);
    void print();
}

class Bag implements WowItem {
    ArrayList<WowItem> items = new ArrayList<WowItem>();
    public String getName(){
        return "背包 ";
    }
    public void add(WowItem item){
        items.add(item);
    }
    public void remove(WowItem item){
        items.remove(item);
    }
    public WowItem get(int i){
        return items.get(i);
    }
    public void print(){
        System.out.println(getName() + "{" );
        for ( WowItem i : items){
            i.print();
        }
        System.out.println("}");
    }
}

class Hearthstone implements WowItem {
    public String getName(){
        return "炉石";
    }
    public void add(WowItem item){ }
    public void remove(WowItem item){ }
    public WowItem get(int i){ return null; }
    public void print(){
        System.out.println(getName());
    }
}

class Dagger implements WowItem {
    public String getName(){
        return "匕首";
    }
    public void add(WowItem item){ }
    public void remove(WowItem item){ }
    public WowItem get(int i){ return null; }
    public void print(){
        System.out.println(getName());
    }
}


class Composite
{
    public static void main(String[] args) {
        WowItem myBag = new Bag();
        WowItem smallBag = new Bag();
        myBag.add(new Dagger());
        myBag.add(smallBag);
        smallBag.add(new Dagger());
        myBag.add(new Hearthstone());
        myBag.print();
    }
}

  为了简化期间,我们构造三个物品:包裹、匕首和炉石。其中,包裹可以容纳子物品,匕首和炉石不可以。为了保持一致性(能在同一容器中容纳它们),它们必须继承于同一个类或者接口,在这个例子上它们均继承于WowItem接口。这个接口声明了4个方法,分别是表示添加、删除、获取某一子物品、打印本物品名称。有几点需要注意:1、我采用了ArrayList容器来保存WowItem,因此所有物品必须继承WowItem。2、在Bag(包裹)类中,均有对add、remove和get做实现,Bag类中的print会调用其所有子节点的print方法。3、对于非Bag类,如Dagger、Hearthstone,它们没有子节点,因此对add、remove、get做了空实现(get返回了null),print方法则直接打印出了它们的名字。

  在main方法中,我们定义了个根节点myBag,为它增加了一把匕首Dagger、一个小背包(smallBag),在小背包中添加了一把匕首,然后再在myBag中添加了一个炉石,程序运行结果如下:

背包 {
匕首
背包 {
匕首
}
炉石
}

  最后再说两个问题:

  一、你可能会发现,对于非组合类(Dagger、Hearthstone),有许多代码是多余的,例如它们不需要实现add、remove、get等方法,如果这些类均继承于WowItem,代码编写会比较繁琐(因为要每个继承的方法都要空实现),而且会损失安全性——我们可能一不小心为一个Dagger调用了add方法或者remove方法。因此,你可以选择为这些非组合类建立一个另外一个类来管理这些子部件,这样的话,就丧失了透明性,它让部分和整体变成了两个独立的部分,我是不推荐这么做的。改善的方法有很多,例如非组合类都继承于一个抽象类A,而这个抽象类A是继承于WowItem的,并且实现了add、remove、get方法,均为抛出一个异常。那么,只要是非组合类调用了add、remove或get,均会得到一个异常。

  二、组合在实际的使用中用途非常广。如在编写界面的时候,用到的控件机制——有些控件可以当做“容器”,例如,Frame中可以放入Button、RadioButton,也可以放入Frame,这就是典型的组合模式的运用。


Java设计模式之从[魔兽世界包裹系统]分析组合(Composite)模式,布布扣,bubuko.com

Java设计模式之从[魔兽世界包裹系统]分析组合(Composite)模式

上一篇:Java设计模式之再从[暗黑破坏神"装备镶嵌宝石系统"]分析装饰(Decorator)模式


下一篇:Java虚拟机内存模型