Eine Möglichkeit, dieses Problem zu lösen, besteht darin, Generika und die new()-Beschränkung zu nutzen.
Anstatt Ihren Konstruktor als Methode/Funktion auszudrücken, können Sie ihn als Fabrikklasse/Interface ausdrücken. Wenn Sie die generische Einschränkung new() auf jeder Aufrufsite angeben, die ein Objekt Ihrer Klasse erstellen muss, können Sie die Konstruktorargumente entsprechend übergeben.
Für Ihr IDrawable-Beispiel:
public interface IDrawable
{
void Update();
void Draw();
}
public interface IDrawableConstructor<T> where T : IDrawable
{
T Construct(GraphicsDeviceManager manager);
}
public class Triangle : IDrawable
{
public GraphicsDeviceManager Manager { get; set; }
public void Draw() { ... }
public void Update() { ... }
public Triangle(GraphicsDeviceManager manager)
{
Manager = manager;
}
}
public TriangleConstructor : IDrawableConstructor<Triangle>
{
public Triangle Construct(GraphicsDeviceManager manager)
{
return new Triangle(manager);
}
}
Wenn Sie es jetzt benutzen:
public void SomeMethod<TBuilder>(GraphicsDeviceManager manager)
where TBuilder: IDrawableConstructor<Triangle>, new()
{
// If we need to create a triangle
Triangle triangle = new TBuilder().Construct(manager);
// Do whatever with triangle
}
Sie können sogar alle Erstellungsmethoden in einer einzigen Klasse konzentrieren, indem Sie eine explizite Schnittstellenimplementierung verwenden:
public DrawableConstructor : IDrawableConstructor<Triangle>,
IDrawableConstructor<Square>,
IDrawableConstructor<Circle>
{
Triangle IDrawableConstructor<Triangle>.Construct(GraphicsDeviceManager manager)
{
return new Triangle(manager);
}
Square IDrawableConstructor<Square>.Construct(GraphicsDeviceManager manager)
{
return new Square(manager);
}
Circle IDrawableConstructor<Circle>.Construct(GraphicsDeviceManager manager)
{
return new Circle(manager);
}
}
Zur Verwendung:
public void SomeMethod<TBuilder, TShape>(GraphicsDeviceManager manager)
where TBuilder: IDrawableConstructor<TShape>, new()
{
// If we need to create an arbitrary shape
TShape shape = new TBuilder().Construct(manager);
// Do whatever with the shape
}
Eine andere Möglichkeit ist die Verwendung von Lambda-Ausdrücken als Initialisierer. Zu einem frühen Zeitpunkt in der Aufrufhierarchie wissen Sie, welche Objekte Sie instanziieren müssen (z. B. wenn Sie einen Verweis auf Ihr GraphicsDeviceManager-Objekt erstellen oder erhalten). Sobald Sie dies wissen, übergeben Sie den Lambda
() => new Triangle(manager)
zu den nachfolgenden Methoden, damit sie wissen, wie sie von da an ein Dreieck erstellen können. Wenn Sie nicht alle möglichen Methoden bestimmen können, die Sie benötigen, können Sie immer ein Wörterbuch der Typen erstellen, die IDrawable mithilfe von Reflection implementieren, und den oben gezeigten Lambda-Ausdruck in einem Wörterbuch registrieren, das Sie entweder an einem gemeinsamen Ort speichern oder an weitere Funktionsaufrufe weitergeben können.
3 Stimmen
Anstatt eine Schnittstelle zu haben, die Ihren Konstruktor definiert, haben Sie stattdessen eine Schnittstelle, die Ihre Fabrikmethoden definiert.