Object-oriented – Dealing with similar objects with different method signatures

design-patternsobject-orientedobject-oriented-design

I am fairly new to OO design and have problems with the design of some software and looking for a pattern or a combination of patterns that could help me solving my problem.

I have a type that has a collection of different geometric shapes (say lines, rectangles, and cubes). I actually even have composites of these shapes.
Each shape has one or more values. A value is a coordinate (between one-dimensional for lines and three-dimensional for cubes).

All types of shapes have two methods that are essential. A method for adding values and a method for getting all values of a shape. E.g. Cube.AddValue(new Value3D{X=4,Y=7,Z=11}) or Line.AddValue(new Value1D{X=42}) respectively Cube.GetValues() would return a collection of Value3D and Line.GetValues() would return a collection of Value1D.

Since these methods have fundamentally different signatures, I can't just derive from a single interface to store all of these different objects in one collection. Splitting the collection into multiple separate collections, one for each type would be possible, but not desirable since they belong together (domain wise).

I've considered using three-dimensional values for all shapes and just ignoring Y/Z for lines/rectangles, but that isn't really nice and would actually violate the O and L in SOLID (a 4th dimension is possibly needed in the future).

Another possibility would probably be to use a generic Interface like IShape<T> where T is Value*D. This interface would look like:

public interface IShape<T> {
    public IEnumerable<T> GetValues();
    public void AddValue(T value);
}

But this would require another, non-generic interface INonGenericShape {} because I can't use a generic interface without specifying the type. I would end up with something like this:

public class SomeShape{
    private IList<INonGenericShape> _shapes = new List<INonGenericShape>();
}

But then I could as well use System.Object, since IShape does not provide any valuable information. So here I am, not knowing how to solve this (storing the objects in a single collection for consumers of that class). Can anyone provide some idea or insight?

Best Answer

Contrary to many (perhaps most) object-oriented design tutorials out there, inheritance hierarchies should not necessarily match up with a domain's real-world taxonomy. In other words, just because all your objects are shapes in the real world doesn't necessarily mean they should all inherit from IShape in your program. Even if they do all share an interface, just because they have similar methods doesn't mean those methods need to be part of that interface.

The trick is to look at the calling code to see what operations you really need at that point. For example:

for shape in shapes:
  shape.doSomething()

Are you really going to call AddValue in a loop like that? I doubt it. More likely, it's going to be something like Draw or GetEditControl, and AddValue will be called before it's even put into shapes, or by a dimension-aware EditControl. In other words, IShape should only have methods that don't care about dimension, and operations that care about dimension should be handled by other interfaces.

Related Topic