views:

168

answers:

6

I want to design shape class. I need to distinguish several different shapes:

-Point  
-Line  
-Triangle  
-Circle  
-Polygons  

The main purpose of this class is to calculate distance between two shapes.
I've all methods for calculating those distances, but I want to have a single method that can be used, it should looks like this:

float Distance(Shape a, Shape b)

Simplest way to do it is to put a lot of if statements and then invoke proper method but this is deffinitely not OOP.

How to design such class in OOP style?

+1  A: 

It depends on what you define as "distance". You could give the base class an abstract CenterPoint property, overridden by each derived class. Now it is simple.

Hans Passant
@nobugz: It doesn't matter how I define distance. I already have methods for calculating them. Maybe it's because what happened today in Poland but I really dont see it :| Lets say we have only point and line, how should Distance(Shape a, Shape b) looks like? You can assume that there are methods DistancePointPoint, DistancePointLine, DistanceLineLine
Tomek Tarczynski
@Tomek: You've said multiple times now that it doesn't matter how you calculate distance, but it obviously does; if you're calculating center distance then all you need to do is get the two center points (which can be implemented as an abstract method) and use the distance formula on those. It's not nearly as simple if you're trying to calculate edge distance.
Aaronaught
@Aaronaught: I'm not calculating distance as a distance between center points. I calculate it a shortes possible distance between points a1 and a2 where a1 belongs to first shape and a2 belongs to second shape
Tomek Tarczynski
@Tomek: you can't use these is a nice OOP manner. At best you can define a virtual DistanceTo(Shape) method so you can at least concentrate the DistancePointXxxx calls in the Point.DistanceTo() method. But you'll have to edit all of them when you add another shape type. My condolences btw, that was extraordinarily tragic.
Hans Passant
@nobugz: Thanks, now I at least know that it can't be done in a OOP manner. I thought that there is some design pattern that covers such situations. Thanks for condolences, it was the biggest tragedy in history of Poland...
Tomek Tarczynski
A: 

If the shapes are all symmetrical on both the X and Y axes, and distance should be calculated from the center, then you'd want to have a Point GetCenter() method in the abstract base class. From there, use the distance formula to calculate the distance between them.

Aaronaught
@Aarounaught: I know how to calculate distance. I've plenty of methods like DistancePointTriangle(Point p, Triangle t) , DistanceLinePolygon(Line l, Polygon p), and so on... But as I said I want to have single method Distance(Shape a, Shape b) which will not used plenty of if statement to determin what type of shape it is.
Tomek Tarczynski
A: 

You could create an abstract base class Shape from which you inherit classes for each of those shapes. In the base class, you declare an abstract method for the functionality you want (for example, CalculateDistance), but without any code (i.e. no body). In each of the inherited classes, you will need to provide implementation for this method.

On a related note: choose the properties and methods for your Shape class in such way that you would need to copy substantial amounts of code from one shape derived class to another. If that would be the case, you should put that code in a property or method in the abstract Shape class. In the end, each subclass only contains code for those portions that differ for each shape. This depends on your implementation, on how to want to represent a shape internally.

Virtlink
+3  A: 

This is a tricky problem, because if you're implementing the method to calculate distance between two objects using the nearest point, you really need to know what are the types of both of the objects. If you were comparing it using for example the center point, then it would be easy - you'd just add GetCenter method, but that simply doesn't work in this case.

The problem is that class hierarchies are useful if you can design them as extensible - that is, allow adding of other types without modifying the existing ones. This is not the case here, because when you add Ellipse you'll need to implement DistancePointEllipse, DistanceTriangleEllipse, and so on... It would be much easier to represent this using algebraic data type known from functional languages. For example in F#:

type Shape = 
  | Circle of float * float * float // center & radius
  | Point of float * float          // center

Then you could use pattern matching to handle all possible cases:

match shape1, shape2 with
| Circle(x1, y1, r1), Circle(x2, y2, r2) -> // two circles
| Point(x1, y1), Point(x2, y2) -> // two points
| Circle(cx, cy, r), Point(px, py) 
| Point(px, py), Circle(cx, cy, r) ->
    // point and a circle (both combinations

Functional programming simply seems to be a better fit for this problem :-).

Anyway, one possible (but still non-extensibel) object-oriented design would be to have Shape base class with methods DistanceToPoint, DistanceToTriangle, etc. that calculate the distance from the current type to the other type of shape (as you really need all the combinations).

Another approach would be to simply write overloaded method in C#:

float Distance(Triangle t, Point p);
float Distance(Triangle t, Circle c);
// ... etc

The nice thing about this option is that you could easily reduce the number of methods you need to write. For example if you have a case for Ellipse and Point, you can inherit Circle from Ellipse and then use the existing case when comparing Circle with Point as well.

Tomas Petricek
+1 for the overloading solution; that's a very clean way to express the crux of the issue, which is that the implementation for any pair of Shapes is going to be different. This also adds compile-time safety, since a Distance between unsupported shapes won't compile. Note that, if you want to take the Distance between unknown Shapes, you'd still need a function with all the messy switching, but at least this gets you better performance/usage for the well-known cases.
Dan Bryant
@Dan Bryant: Good point that this will work only for statically known types. I suppose you could use C# 4 `dynamic` to make it work for unknown types too (and I don't think that would be _particularly bad_ solution actually).
Tomas Petricek
+1  A: 

One design that could make this extensible would be to encapsulate the distance finding as a Strategy that operates on particular Shapes. You would register delegates for new Shape type pairs, which the system could then use to resolve an appropriate function for any pair of shape types. This would allow for extensibility, but it would also increase complexity and decrease performance. If your hierarchy is as simple as you've described, then simple conditionals will be both faster and easier to maintain.

The real issue here is that the function in question is not a simple behavior of a specific class, but rather involves knowledge about all of the Shapes in the system. The best you can do for 'OOP' is encapsulate all the messy conditionals in a manager class that knows about all of your Shapes. You can't avoid the necessary coupling that is introduced as a result of providing this function, but you can at least hide it behind a simpler interface, as you've described.

Dan Bryant
A: 

Make an abstract Shape base class with a flatten method, that converts the shape to polygons:

abstract class Shape
{
    virtual Polygons Flatten();
}

The code to calculate the distance between two polygons should be easy enough. Then your Distance function looks like this:

float Distance(Shape a, Shape b)
{
    Polygons polygonsA = a.Flatten();
    Polygons polygonsB = b.Flatten();
    return polygonsA.Distance(polygonsB);
}
GarethOwen