我在尝试完成需要使用策略和复合模式的实践时遇到了麻烦。我正在尝试创建一系列车辆,这些车辆根据其所在的表面可能具有不同的行为。然而,这些车辆在地面上可能有不止一种行为 - 例如,如果天气条件设置为下雪和下雨,它们可能会同时下雪和下雨。
我有一个名为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”部分无效 - 我必须在每个行为中添加一个空构造函数。
我可以在我创建的代码中看到明显的问题,我只是不太明白如何修复它们。
答案 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();
}
}
如果您的策略有不同抽象,那么效果很好。在这种情况下,Flying
和BirdCalling
彼此无关,但允许它们在运行时通过实现而变化(嘎嘎叫,唧唧喳喳或飞行,不飞行等)
但是,如果您希望动态创建不同的实例而不进行子类型化,则可能需要查看Decorator pattern。装饰器模式允许您将“功能”的任意组合应用于运行时的实例。
因此,您最终可能会得到一个实例化的对象,例如:
Window decoratedWindow = new HorizontalScrollBarDecorator (
new VerticalScrollBarDecorator(new SimpleWindow()));