Design model for managing the interaction between different types of objects

advertisements

I have a C# program that has a class called Ball that has two enum fields called BallColor and BallType. There are 7 ball colors (red, blue, yellow, etc.) and 7 ball types (tennis, soccer, bowling, etc.). A ball object can have any of the possible combinations of color and type.

In my program, I have many Ball objects of different color and type combinations. Balls can interact with each other via collisions. When two balls collide, it triggers an interaction that is handled by a function called handleInteraction(ball1, ball2). The interaction between the two balls depends on the type and color of each ball. For example, if any bowling ball hits any ping pong ball, the ping pong is destroyed and the bowling ball continues to move with its original velocity. However, if any soccer ball collides with a bowling ball, the soccer ball bounces off the bowling ball and the bowling ball's velocity is reduced by n. Another example, if a red tennis ball collides with a green ping pong ball, the ping pong ball bounces off the tennis ball and the color of the ping pong ball is changed to the same color of the tennis ball (red).

In my handleInteraction(ball1, ball2) function, I've been handling the interactions using nested switch statements. However, as I write more code to handle all of the scenarios, nested switch statements doesn't seem to be the right approach.

Can anyone think of a better way to handle the interaction between balls?


One solution is to create interface for Interaction result and for ball:

public interface IInteractionResult
{
    void Handle();
}

public interface IBall
{
    BallTypeEnum BallType { get; }

    IInteractionResult HandleInteraction(IBall ball);
}

And implement every of your possible classes which implements IInteractionResult where the logic for interaction provided at Handle method:

public class DestroyInteractionResult : IInteractionResult
{

    public void Handle()
    {
        //your code for destroy result behaviour
    }
}

public class BounceInteractionResult : IInteractionResult
{

    public void Handle()
    {
        //your code for bounce result behaviour
    }
}

public class NothingInteractionResult : IInteractionResult
{

    public void Handle()
    {
        //your code for nothing
    }
}

After this implement your classes for Ball which will implement IBall with switch statement at implementation of HandleInteraction. SoccerBall for example:

public class SoccerBall : IBall
{
    public BallTypeEnum BallType
    {
        get { return BallTypeEnum.Soccer; }
    }

    public IInteractionResult HandleInteraction(IBall ball)
    {
        switch (ball.BallType)
        {
            case BallTypeEnum.Soccer:
                return new BounceInteractionResult();
            case BallTypeEnum.Bowl:
                return new DestroyInteractionResult();
            case BallTypeEnum.PingPong:
                return new BounceInteractionResult();
            // and so on
        }

        throw new NotImplementedException("Undefined ball type");
    }
}

Separate classes for each ball type and for each interaction helps you to collect single logic for each type into single class.

And you handleInteraction would look like:

public void handleInteraction(IBall ball1, IBall ball2)
{
    var interaction = ball1.HandleInteraction(ball2);

    interaction.Handle();
}

In my opinion it is most flexible solution. Some kind of Strategy pattern.