无法实施策略和复合模式以在游戏中提供不同的行为

时间:2013-03-19 22:51:59

标签: java composite strategy-pattern

我在尝试完成需要使用策略和复合模式的实践时遇到了麻烦。我正在尝试创建一系列车辆,这些车辆根据其所在的表面可能具有不同的行为。然而,这些车辆在地面上可能有不止一种行为 - 例如,如果天气条件设置为下雪和下雨,它们可能会同时下雪和下雨。

我有一个名为AbstractVehicle的类,它有两个具体的子类,Car和Boat。

然后我有一个名为IBehaviour的界面。实现此接口是两个名为LandBehaviour和WaterBehaviour的抽象类(它们是复合模式的顶层)。其中每个都有一个子类集合。仅关注LandBehaviour,它的子类是SnowBehaviour,StandardBehaviour和其他一些包括LandAssembly。

我的想法是,我会在LandBehaviour中将代码放在复合的上层。然后,每个具体的子类都将具有add,remove和list部分复合的空实现,LandAssembly类包含实际将各种行为组合在一起所需的代码。

这是为了产生一个结果,例如,汽车可以同时拥有StandardBehaviour和SnowBehaviour。

我希望对我试图实现的基本结构有一些反馈,而不是发布大量代码(并且有很多代码)。我现在收到一些错误,例如空指针异常,而不是花了很长时间试图修复它们,我想知道项目的布局是否正确。

编辑:添加代码 - 生成空指针异常

这是我的AbstractVehicle类:

public AbstractVehicle (IBehaviour behaviourIn) {
    behaviour = behaviourIn;
}

public void setBehaviour(IBehaviour ib) {
    behaviour = ib;
}

public IBehaviour getBehaviour() {
    return behaviour;
}

public void move() {
    behaviour.ensureCorrectBehaviour();
}

汽车子类:

public Car () {
    super(new StandardBehaviour());
}

IBehaviour界面:

public interface IBehaviour {
    public void ensureCorrectBehaviour();
}

LandBehaviour抽象类:

public void ensureCorrectBehaviour() {
}

public ILandBehaviour () {
}

private ILandBehaviour landBehaviour;

public ILandBehaviour (ILandBehaviour landBehaviour) {
    this.landBehaviour = landBehaviour;
}

public ILandBehaviour getBehaviour() {
    return landBehaviour;
}

public abstract void addBehaviour(ILandBehaviour behaviour);
public abstract void removeBehaviour(ILandBehaviour behaviour);
public abstract ILandBehaviour[] getBehaviours();

具体行为子类(RacingBehaviour)的一个例子:

public RacingBehaviour(ILandBehaviour landBehaviour) {
    super(landBehaviour);
}

public RacingBehaviour() {}

@Override
public void ensureCorrectBehaviour() {
    System.out.println("Vehicle is racing.");
}

public void addBehaviour(ILandBehaviour behaviour) {}
public void removeBehaviour(ILandBehaviour behaviour) {}
public ILandBehaviour[] getBehaviours() {
    return null;
}

最后是LandAssembly类:

public class LandAssembly extends ILandBehaviour {

private List<ILandBehaviour> behaviours;

public LandAssembly(ILandBehaviour landBehaviour) {
    super(landBehaviour);
    behaviours = new ArrayList<ILandBehaviour>();
}

public LandAssembly() {}

public void addBehaviour(ILandBehaviour behaviour) {
    behaviours.add(behaviour);
}

public void removeBehaviour(ILandBehaviour behaviour) {
    behaviours.remove(behaviour);
}

public ILandBehaviour[] getBehaviours() {
    return behaviours.toArray(new ILandBehaviour[behaviours.size()]);
}   
}

我正在使用这个跑步者:

    AbstractVehicle aCar = new Car(120);
    aCar.move();

    ILandBehaviour snow = new SnowBehaviour();
    ILandBehaviour racing = new RacingBehaviour();
    ILandBehaviour as = new LandAssembly();
    as.addBehaviour(snow);
    as.addBehaviour(racing);

在我实施复合材料之前,一切都很好。我能够使用客户端创建一个新车,调用其move()方法,然后改变其行为,再次调用move()并查看差异。但我知道我现在有点在我的复合模式实现中离开ensureCorrectBehaviour()方法,这显然是错误的。我也知道在执行此操作后,Car构造函数的“new”部分无效 - 我必须在每个行为中添加一个空构造函数。

我可以在我创建的代码中看到明显的问题,我只是不太明白如何修复它们。

1 个答案:

答案 0 :(得分:0)

如果您担心设计模式,那么类图将非常有用。您有许多功能,并将这些功能分组到更高级别的抽象(例如雪/地/水/等)。但您的车辆只采取一种行为。车辆是否需要具有多种功能? (当然,就像你提到的那样)。

您可能会考虑在您的班级中制定具体定义的策略,其中策略的每个实施都可能有所不同。

public abstract class Bird
{
    protected BirdCallStrategy callStrat;
    protected FlyStrategy flyStrat;
}

public class Duck
{
    public Duck()
    {
        callStrat = new QuackStrategy();
        flyStrategy = new FlySouthForWinterStrategy(TimeOfYear);
    }
}

public class Chicken
{
    public Chicken()
    {
        callStrat = new CluckStrategy();
        flyStrat = new NoFlyStrategy();
    }
}

如果您的策略有不同抽象,那么效果很好。在这种情况下,FlyingBirdCalling彼此无关,但允许它们在运行时通过实现而变化(嘎嘎叫,唧唧喳喳或飞行,不飞行等)

但是,如果您希望动态创建不同的实例而不进行子类型化,则可能需要查看Decorator pattern。装饰器模式允许您将“功能”的任意组合应用于运行时的实例。

因此,您最终可能会得到一个实例化的对象,例如:

Window decoratedWindow = new HorizontalScrollBarDecorator (
                new VerticalScrollBarDecorator(new SimpleWindow()));