4

I have been developing a library with the help of another library.

So what I have set up here is two classes sub-classing the library's classes for additional functionality. The problem comes in when I want to be able to use the classes interchangeably in another class. how sad am i So I want to be able to pass these graphics into my object through the constructor as long as it is of type "super class". The problem is, Rectangle and Circle have different property names for setting their position and in my object I need to set their position. I could check the type then cast, but then if someone wants to pass in a graphic like triangle, they'd have to edit the class to check for type triangle.

What I chose to do instead was create an interface ICenterable that my circle and rectangle class implement that requires a CenterX and CenterY property. Now anyone who has an ICenterable object can pass it into the object. This would be perfect, except now I have to choose whether the parameter being passed will be types as ICenterable or "super class". Once again, I could cast when needing certain functionality, but what if, for example, a "super class" is passed in that doesn't implement ICenterable. In the constructor I could check to make sure it's of both types or throw an error but that seems sloppy.

The best solution would be if I could state that the object being passed must implement ICenterable and extend "super class". I would do this through making a class that does both, but then my circle and rectangle classes wouldn't be able to extend that class because they need to extend Circle and Rectangle. enter image description here It seems like the more I do the worse it gets. Should I just accept it's not going to be how I want, forget the interface, and just check whether it's a rectangle, circle, and add more later if needed? Or is there a better way of doing it?

Thanks

  • 1
    The best solution I've seen to this is to use the adapter pattern and make wrappers for everything. It's a pain in the ass. – Alexander - Reinstate Monica Sep 14 at 0:12
  • 2
    "The best solution would be if I could state that the object being passed must implement ICenterable and extend super class." Why do you need it to extend anything if it implements the interface you need? – Martin Maat Sep 14 at 5:51
5

Instead of inheriting, try wrapping. Use a generic wrapper so you can specify constraints.

interface IWrapper
{
    void Center();
}

interface IWrapper<T> : IWrapper where T : Superclass
{
    T AsOriginalItem();
}

abstract class WrapperBase<T> : IWrapper<T>
{
    protected readonly T _wrappedItem;

    public WrapperBase(T wrappedItem)
    {
        _wrappedItem = wrappedItem;
    }

    abstract public Center();

    public T AsOriginalItem()
    {
        return _wrappedItem;
    }        
}

class RectangleWrapper : WrapperBase<Rectangle>
{
    public RectangleWrapper(Rectangle item) : base(item) {}

    public override void Center()
    {
        var originalRectangle = this.AsOriginalItem();
        originalRectangle.MoveTo(etc....);
    }
}


class CircleWrapper : WrapperBase<Circle>
{
    public CircleWrapper(Circle item) : base(item) {}

    public override void Center()
    {
        var originalCircle = this.AsOriginalItem();
        originalCircle.MoveTo(etc....);
    }
}

Now when you want to pass a shape to the constructor of, say, Foo, you could do this:

class Foo
{
    public Foo(IWrapper wrapper)
    {
        wrapper.Center();
    }
}

var foo1 = new Foo(new RectangleWrapper(originalRectangle));
var foo2 = new Foo(new CircleWrapper(originalCircle));

...and you're type-safe.

  • In the example at the end (Foo constructor) you mean "IWrapper" instead of the "IWrapperBase", right? (I tried to edit this in, but it's less than 6 characters so I can't). – Vector Zita Sep 15 at 0:59
2

If I am getting this right, then your posted case is only a small part of the problem you are facing. If Circle and Rectangle have different methods for setting the position, then what is it that the Superclass is abstracting away (which, I guess, is something along the lines of Shape)?

If, for your specific use case, Circle and Rectangle are already different in terms of implementation, then the Superclass type is not really helping you (i.e. it does not define the useful single property/method you desire from all implementations). You should consider redefining the type and potentially wrapping those external-library types (shapes) as extensions of your own type, as John Wu advised. In this way, you can define your own uniform set of properties/methods that you need all shapes to have.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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