Question

I'm running into real trouble trying to complete a practical that requires using strategy and composite pattern. I am trying to create a collection of vehicles which can have different behavior depending on the surface they are on. However, these vehicles can have more than one behaviour on a surface - for example, they could have snow drive and rain drive at the same time, if the weather conditions are set to snow and rain.

I have a class called AbstractVehicle, which has two concrete subclasses, Car and Boat.

I then have an interface called IBehaviour. Implementing this interface is two abstract classes called LandBehaviour and WaterBehaviour (which are the top tier of the composite pattern). Each of these have a collection of subclasses. Focussing solely on LandBehaviour, its subclasses are SnowBehaviour, StandardBehaviour and a few others including LandAssembly.

The idea was that I would put the code for the upper-tier of composite in LandBehaviour. Then, each of the concrete subclasses would have empty implementations of the add, remove and list parts of composite, with the LandAssembly class containing the code needed to actually combine various behaviours together.

This is intended to produce the result that, for example, a car could have both StandardBehaviour and SnowBehaviour at the same time.

Rather than posting large amounts of code (and there is a lot of it), I was hoping for some feedback on the basic structure I am trying to implement. I am getting a few errors right now such as null pointer exceptions and rather than spent a long time trying to fix them, I wanted to get an idea on whether the layout of the project was right to begin with.

Edit: Adding code - which generates a null pointer exception

This is my AbstractVehicle class:

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

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

public IBehaviour getBehaviour() {
    return behaviour;
}

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

The car subclass:

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

The IBehaviour interface:

public interface IBehaviour {
    public void ensureCorrectBehaviour();
}

The LandBehaviour abstract class:

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();

An example of a concrete behaviour subclass (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;
}

And finally the LandAssembly class:

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()]);
}   
}

I am using this runner:

    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);

Before I implemented the composite, everything was fine. I was able to use the client to create a new car, call its move() method, then change its behaviour, call move() again and see the difference. I'm aware however that I'm now kinda leaving the ensureCorrectBehaviour() method in my implementation of the composite pattern, which is obviously wrong. I'm also aware that after doing this, the "new" part of the Car constructor didn't work - I had to add an empty constructor each behaviour.

I can see glaring problems in the code I've created, I just don't quite see how to fix them.

Was it helpful?

Solution

If you are concerned about the design patterns, a class diagram would be extremely useful. You have many features, and you group those features into higher levels of abstractions (such as snow/land/water/etc.) But your vehicle only takes in one behavior. Does a vehicle need to be able to have multiple features? (Surely it does as you mention).

You might consider having concretely-defined strategies in your class, where each implementation of the strategy can vary.

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();
    }
}

This works well if you have distinct abstractions for your strategies. In this case Flying and BirdCalling have nothing to do with each other, but they are allowed to vary by implementation at runtime (Quacking, chirping or flying, not flying, etc.)

If however, you want to create varying instances on the fly without subtyping, you might want to look into the Decorator pattern. The decorator pattern allows you to apply any combination of "features" to an instance at run-time.

So you might end up with an object that is instantiated such as:

Window decoratedWindow = new HorizontalScrollBarDecorator (
                new VerticalScrollBarDecorator(new SimpleWindow()));
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top