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

来源:互联网 发布:电脑自动改mac地址 编辑:程序博客网 时间:2024/05/16 02:34

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

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

  以上的模型反应了组合(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,这就是典型的组合模式的运用。


0 0
原创粉丝点击