dimanche 24 janvier 2021

How to derive from/implement a C# class hierarchy in without code duplication

I am faced with a C# design problem that C#'s limitations are making hard to achieve. I need some sort of design pattern/strategy to rescue me.

I must create the archetypical set of abstract shape classes: Base class Shape with derived classes LineShape, RectShape, etc. Specific implementations will derive from these. The problem is that I really need to use classes here, not interfaces and this is forcing me to duplicate a lot of code.

To illustrate:

public abstract class Shape
{
    public abstract int      Id           { get; }      // Unique ID 
    public abstract string   Name         { get; set; } // Shape name.  Defaults to the type of shape
    public abstract bool     IsLocked     { get; set; } // May the user change this shape in any way?
}

public abstract class LineShape : Shape
{
    public abstract Point P1 { get; set; }
    public abstract Point P2 { get; set; }
}

public abstract class CircleShape : Shape
{
    public abstract Point Center { get; set; }
    public abstract double Radius { get; set; }
}

When I start creating the derived implementations (LineImpl, CircleImpl, etc), I find that the implementations of the Shape functions are identical for all the Impl classes, yet specific enough that I cannot implement them in the abstract Shape class itself.

So I need to find a way to share a common implementation of these function in my derived hierarchy.

In other words LineImpl must derive from LineShape. CircleImpl must derive from CircleShape, etc. I cannot find a way to insert a ShapeImpl in there to handle the boiler plate stuff. So I am forced to implement all those Shape functions over and over again, once in each Impl shape type.

I tried using generics to get my way out of this but unfortunately a generic class cannot specify its base class with a generic argument. In other words, the following approach (which I might do in C++) does not work in C#

public class ShapeImpl<TBase> : T where T : Shape {  ... boiler plate implemented here)...}
public class LineImpl         : ShapeImpl<LineShape> { }
public class CircleImpl       : ShapeImpl<CircleShape> { }

So I am at a loss. Is there some C# trick or design pattern that can get me out of this? Or am I forced to implement the same functions multiple times?

Aucun commentaire:

Enregistrer un commentaire