Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

I was posed with the idea of creating an object that checks if a point will collide with a line:

public class PointAndLineSegmentCollisionDetector {
    public void Collides(Point p, LineSegment s) {
        // ...
    }
}

This made me think that if I decided to create a Box object, then I would need a PointAndBoxCollisionDetector and a LineSegmentAndBoxCollisionDetector. I might even realize that I should have a BoxAndBoxCollisionDetector and a LineSegmentAndLineSegmentCollisionDetector. And, when I add new objects that can collide I would need to add even more of these.

But, they all have a Collides method, so everything I learned about abstraction is telling me, "Make an interface."

public interface CollisionDetector {
    public void Collides(Spatial s1, Spatial s2);
}

But now I have a function that only detects some abstract class or interface that is used by Point, LineSegment, Box, etc.. So if I did this then each implementation would have to to a type check to make sure that the types are the appropriate type because the collision algorithm is different for each different type match up.

Another solution could be this:

public class CollisionDetector {
    public void Collides(Point p, LineSegment s) { ... }
    public void Collides(LineSegment s, Box b) { ... }
    public void Collides(Point p, Box b) { ... }
    // ...
}

But, this could end up being a huge class that seems unwieldy, although it would have simplicity in that it is only a bunch of Collide methods.

This is similar to C#'s Convert class. Which is nice because it is large, but it is simple to understand how it works. This seems to be the better solution, but I thought I should open it for discussion as a wiki to get other opinions.

share|improve this question
add comment

3 Answers 3

up vote 3 down vote accepted

A generic interface would fit well here

public interface ICollisionDetector<TFirst, TSecond>
{
    bool Collides(TFirst first, TSecond second);
}

public class PointAndBoxCollisionDetector : ICollisionDetector<Point, Box>
{
    public bool Collides(Point first, Box second)
    {
        // Collision logic
    }
}

You can use a FactoryMethod to generate your detector, based on the types of the two objects you have.

public class CollisionDetectorFactory
{
    public ICollisionDetector<TFirst, TSecond> Create<TFirst, TSecond>()
    {
         // Here you can have a giant switch/case, or 
         // you can map using reflection, or you can get
         // from an IoC Container/Service Locator.
    }
}
share|improve this answer
    
Could you extend the answer to show what using reflection and using an IoC Container/Service locator would look like? –  Joshua Harris Nov 11 '12 at 20:40
add comment

Theory

One should implement a common interface when:

  • Objects are similar and have a similar logic, and:

  • The shared logic is used in other places where the distinction between the types is not required.

For example, one shouldn't implement a common interface for both Dog: Animal and RSS, just because both have a common method Feed. Not only the logic behind those two methods is different (new Dog(...).Feed() would launch a robot which will give some food to a dog, while new RSS(...).Feed() will simply download the most recent RSS from the server), but you can hardly imagine a common Feeder which would be responsible of controlling dog food and downloading the last RSS feeds.

On the other hand, it would be convenient to have a common interface for both new Dog(...).Feed() and new Goldfish(...).Feed(), because while the implementation would be different, since the robot is expected to not putting a bowl of meat into an aquarium, the common interface still allows for the callers to not make any difference between the animals:

var myPets = new [] { (IAnimal)new Dog(...), new Goldfish(...) };
...
new FeedingEngine().FeedPets(myPets);

public class FeedingEngine
{
    public void FeedPets(IEnumerable<IAnimal> pets)
    {
        foreach (var pet in pets)
        {
            if (pet.Status == AnimalStatus.Alive && pet.IsPotentiallyHungry)
            {
                pet.Feed();
            }
        }
    }
}

Your case

Both of your proposals are correct. From the point of view of the consumer of ICollisionDetector, all I need to know is that I can pass any pair of spacial objects and get the collision result, no matter how.

There is a slight difference between those approaches: it's the fact that the first proposal requires you to double-check if all objects inheriting ISpatial are handled correctly by the collision detector. It can be error prone later: if, working on your code later, another developer implements another spatial object, let's say Sphere: ISpatial, but forgets to implement the collision logic, Collides(sphere, box) will fail, and it would be difficult to understand why is it failing, especially if the collision detector is used indirectly, like through the game engine.

The second approach is an opt-in. If the collision detector doesn't have a Collides(Sphere s, Box b), nobody would be able to call such a conversion. This being said, this itself creates another problem: you will not be able to use generics or any high-level abstraction. For example:

var box = new Box(...);
var torus = new Torus(...);

var engine = new GameEngine();
engine.Show(new [] { box, torus });
engine.StartColliding(); // Moves objects to the center of the map and stops on collision.

is easy to implement with your first proposal if both Box and Torus implement ISpatial, but would be extremely difficult to do with your second proposal.

In order to be able to use your first proposal while reducing the issue I noted above, here's a third proposal:

public interface ISpatial
{
    bool IsColliding(ISpatial obj);
}

public class CollisionDetector
{
    public bool IsColliding(ISpatial s1, ISpatial s2)
    {
        return s1.IsColliding(s2);
    }
}

public class Torus : ISpatial
{
    public bool IsColliding(ISpatial obj)
    {
        // Check that obj is not null.

        // Do the collision logic for known types.
        var box = obj as Box;
        if (box != null)
        {
            // Do the collision logic.
            return result;
        }

        return obj.IsColliding(this);
    }
}

public class Cube : ISpatial
{
    public bool IsColliding(ISpatial obj)
    {
        // Check that obj is not null.

        // Do the collision logic for known types.

        return obj.IsColliding(this);
    }
}

Since the cube class is created first, it is unaware of torus; instead of throwing a NotImplementedException, it simply delegates the collision detection logic to the torus, which is well aware of the cube object, since the torus class was the second one to be created.

Note: of course, you still have to handle the situations where neither the first, nor the second object are aware of each other (for example if two objects were created by two different developers at the same moment of time). Actual code above will simply overflow and crash, but you can easily change it to throw NotImplementedException instead.

share|improve this answer
add comment

Programmingly speaking, pdr's solution offers the best design but I would have suggested first to use an IoC container (configured to return single instances), then using reflection if not possible and really lastly using a switch/case.

Now mathematically speaking, I think there are maybe more optimal solutions to this problem. For example, if your goal is really to just detect the collision between several objects in motion an be able to resolve theses collisions, for example in a game like 'angry birds', there are many optimal algorithms.

I'd suggest that maybe you should expose the problem you are trying to solve on http://gamedev.stackexchange.com/ Since for me it's more a math/physics programming problem than a 'pure' programming problem.

It would be a shame to design a complex Object Oriented solution whilst there is maybe a simpler (and with better performance) mathematical solution.

share|improve this answer
    
Agreed. That will help when I am actually working on this, but this was the best example I had to ask the question about interfaces. Could you explain why you think an IoC container would be better? –  Joshua Harris Nov 11 '12 at 20:39
    
Using a IoC container will save you the time to write all the reflection code to fetch all the concrete implementations, plus IoC container offers out of the box instance life time strategies. By using an IoC container, you will respect the Open Closed principle. This mean that any time you will need a new type of Collision Detection, you will just have to write the new implementation without having to touch any existing code. If you don't use an IoC container and use a "big switch/case", any time you add a new type of Collision detection, you will have to update that big switch/case. –  sigo Nov 11 '12 at 21:31
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.