implementing a new interface without creating a derived type
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 orig开发者_如何学Goinal 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?
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.
The decorator Pattern might be useful here. This does not get round the problem of many implmenting classes.
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.
精彩评论