Tomek Tarczynski - 3 months ago 26

C# Question

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?

Answer

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.