views:

71

answers:

3

Hello,

I have a several C# classes called ShapeA, ShapeB, ShapeC, etc. and collection class called ShapeCollection. they all inherit from an abstract class called Geometry. All of these classes arrive from a 3rd party assembly, so I can't change them.

I want too add a new method to all of these Shape classes, let's call it Paint(), and implement this method in a different way for each Shape class. The ShapeCollection class will just call this Paint() method on each shape class in the collection.

The most straight-forward way I thought of doing this is to make a new class for each shape class that will inherit from the original shape class but will also implement an interface that will contain the Paint() method.

I'm really trying to avoid in creating a derived type for each of these shape, because there are many shape classes.

What is the correct way to do this? Any help will be appreciated.

Thank you,

Kathy

+2  A: 

Why not use an extension method?

namespace YourProject.Extensions
{
    public static class ShapeExtensions
    {
        public static void Paint(this ShapeA shape)
        {
            // Your paint code
        }
        public static void Paint(this ShapeB shape)
        {
            // Your paint code
        }
        public static void Paint(this ShapeC shape)
        {
            // Your paint code
        }
    }
}

Then there's no need to create a new class that inherits from the Shape classes.

Then in your ShapeCollection, you can just call the methods as normal (remember the include the namespace of where the extension methods reside in your using statements).

UPDATE:

Just to satisfy the critics - this will only work if you're dealing with the actual classes themselves. I.e. if you're doing:

ShapeA shape = new ShapeA();
shape.Paint(); // This will work

But:

Geometry shape = new Shape();
shape.Paint(); // This will not exist

The extension methods will only be visible when you use the class directly.

GenericTypeTea
This would not work with her `ShapeCollection`, unless it used reflection to find the extension method.
Ani
@Ani - it'll work fine. Unless I've misunderstood the question, this will work perfectly.
GenericTypeTea
@GenericTypeTea: You can not do: Geometry a = new ShapeA(); a.Paint(); Then you have to create an Extension method on the Geometry class to that dispatches to the right Paint in the right type. But that will be quite messy.
Albin Sunnanbo
@ GenericTypeTea: If you used extension methods, how would you implement a `ShapeCollection<Geometry>: ICollection<Geometry>` or a `ShapeCollection<T>:ICollection<T> where T:Geometry` that contains a `PaintAll method`?
Ani
@Ani @Albin Sunnanbo - Updated the answer to satisfy your critisms. As the OP didn't give any information on how the ShapeCollection works (and still hasn't), this answer was only a *suggestion*.
GenericTypeTea
This would be my choice until the paint logic becomes complicated to comfortably fit in a method, then I'd bite the bullet and go Decorator.
batwad
Correction: That should have been "`ShapeCollection: ICollection<Geometry>` or a `ShapeCollection<T>: ICollection<T> where T:Geometry`"
Ani
Thanks for the answers, but the critics are right, and that is why I haven't used extension methods. See my comment to the original post for info on the ShapeCollection.
Kathy S
Couldn't you solve the problem with this admittedly ugly additional extension method? It's getting pretty desperate at this point though, and you really shouldn't be scared of creating extra classes.public static void Paint(this Geometry shape){ if (shape is ShapeA) ((ShapeA)shape).Paint(); else if (shape is ShapeB) ((ShapeB)shape).Paint(); else if (shape is ShapeC) ((ShapeC)shape).Paint(); else throw new InvalidOperationException("Can't paint " + shape.GetType().FullName);}
batwad
+1  A: 

The decorator Pattern might be useful here. This does not get round the problem of many implmenting classes.

mR_fr0g
A: 

I tried putting this in a comment on GenericTypeTea's answer but the formatting didn't come out right.

Couldn't you solve the problem with this admittedly ugly additional extension method? It's getting pretty desperate at this point though, and you really shouldn't be scared of creating extra classes.

public static void Paint(this Geometry shape)
{
    if (shape is ShapeA)
        ((ShapeA)shape).Paint();
    else if (shape is ShapeB)
        ((ShapeB)shape).Paint();
    else if (shape is ShapeC)
        ((ShapeC)shape).Paint();
    else
        throw new InvalidOperationException("Can't paint " + shape.GetType().FullName);
}

Alternatively you could tidy the ugliness up with a simple class hierarchy and a factory method.

interface IShapePainter
{
    void Paint(Geometry shape);
}

static class ShapeExtensions
{
    public static IShapePainter GetPainter(Geometry shape)
    {
        if (shape is ShapeA)
            return new ShapeAPainter();
        // Add other painters here
        else
            return null;
    }

    public static void Paint(this Geometry shape)
    {
        GetPainter(shape).Paint(shape);
    }
}

abstract class ShapePainter<T> : IShapePainter
    where T : Geometry
{
    public abstract void Paint(T shape);

    void IShapePainter.Paint(Geometry shape)
    {
        this.Paint((T)shape);
    }
}

class ShapeAPainter : ShapePainter<ShapeA>
{
    public override void Paint(ShapeA shape)
    {
        // Your paint code
    }
}

You'd then need to update GetPainter with each implementation of ShapePainter corresponding to all the shapes you can paint. The generic class isn't strictly necessary as you could implement the interface directly on ShapeAPainter, but it saves you duplicating the cast every time you implement it.

batwad