Designfrage
-
Hallo,
Es gibt eine abstrakte Basisklasse "Base", diese benötigt zwingend einen Wert für "RequiredMember".
abstract class Base { int m_requiredMember; public RequredMember { get{return m_RequiredMember;} private set{m_RequiredMember = value;} } }
Wie kriegt man den Member da am besten rein?
Mir fallen dazu zwei Möglichkeiten ein, die mir aber beide nicht gefallen-
Einen "Base"-Konstruktur anlegen, der den Parameter erwartet.
Vorteil: Der Member bekommt auf jeden Fall seinen Wert
Nachteil: Jede abgeleitete Klasse braucht ebenfalls einen Konstruktor, der den Parameter
erwartet und ihn dann an die Basisklasse weiterreicht (=Redundanz) -
Die Basisklasse bekommt eine statische Methode:
public static Base Create<T>(int Value) where T:Base, new() { T instance = new T(); instance.RequredMember = Value; return instance; }
Vorteil: es braucht nicht jede abgeleitete Klasse den Parameter "nach oben" durchreichen
Nachteil: eine abgeleitete Klasse kann auch ohne den Aufruf von "Base.Create<..>()" erzeugt werden => kein Wert für den Member.Frage:
Wie schaut die Musterlösung aus?
-
-
Soll 'requiredMember' nur jeweils unterschiedlich für jede abgeleitete Klasse sein oder beliebig für jedes mögliche Objekt?
Aber mir scheint das Design generell etwas merkwürdig zu sein. Für was genau benutzt du dann 'requiredMember'?
-
Der Wert kann für jedes Objekt einer (abgeleiteten) Klasse unterschiedlich sein.
Mir fällt gerade auf, dass sich das Problem ja eigentlich schon bein den Lehrbuchbeispielen ergeben sollte. Hm, dann weiss ich zumindest schon mal, nach was ich Google bemühen kann.
abstract class Animal { int age; ... } class Dog : Animal {}
Konkret brauch ich's für eine Statemachine, die Basisklasse stellt einen Übergang dar, die benötigten Members sind Quell- und Folgezustand.
-
anonymus schrieb:
- Einen "Base"-Konstruktur anlegen, der den Parameter erwartet.
Vorteil: Der Member bekommt auf jeden Fall seinen Wert
Nachteil: Jede abgeleitete Klasse braucht ebenfalls einen Konstruktor, der den Parameter
erwartet und ihn dann an die Basisklasse weiterreicht (=Redundanz)
Wieso soll das Redundant sein?
Vielleicht stellen einige abgeleitete Klassen das "required member" ja selbst bereit?Genau das, also dem Basisklassen-ctor einfach ein Parameter verpassen, ist "Standard".
- Die Basisklasse bekommt eine statische Methode:
public static Base Create<T>(int Value) where T:Base, new() { T instance = new T(); instance.RequredMember = Value; return instance; }
Vorteil: es braucht nicht jede abgeleitete Klasse den Parameter "nach oben" durchreichen
Nachteil: eine abgeleitete Klasse kann auch ohne den Aufruf von "Base.Create<..>()" erzeugt werden => kein Wert für den Member.Weiterer Nachteil: es ist ordentlich verwirrend.
Frage:
Wie schaut die Musterlösung aus?Siehe oben: dein 1) ist die Musterlösung.
p.S.:
- Alle Klassen bleiben Default-konstruierbar, und es gibt auch keine Factory-Funktion ala (2).
Finde ich grundsätzlich ... nicht so gut. Einzige Ausnahme wäre, wenn man Data-Driven Dependency-Injection macht.
- Einen "Base"-Konstruktur anlegen, der den Parameter erwartet.
-
anonymus schrieb:
Nachteil: eine abgeleitete Klasse kann auch ohne den Aufruf von "Base.Create<..>()" erzeugt werden => kein Wert für den Member.
Du kannst alle Konstruktoren protected machen, dann geht nur Create<>
-
abstract class Animal { public Animal(int maximumAge) { MaximumAge = maximumAge; } public int MaximumAge { get; set; } } public class Dog : Animal { public Dog() : base(16) { } } public class Bunny : Animal { public Bunny() : base(9) { } }
ich erkenn da keine redudanz
-
Mr Evil schrieb:
abstract class Animal { public Animal(int maximumAge) { MaximumAge = maximumAge; } public int MaximumAge { get; set; } } public class Dog : Animal { public Dog() : base(16) { } } public class Bunny : Animal { public Bunny() : base(9) { } }
ich erkenn da keine redudanz
ich schon:
public class Dog : Animal { public Dog(int Age) : base(Age) { } } public class Bunny : Animal { public Bunny(int Age)) : base(Age) { } }
Jetzt braucht jede abgeleitete Klasse den Age Parameter, obwohl sie diesen selbst nicht braucht, sondern nur an die Basisklasse durchwinkt.
Aber der Vorschlag von loks mit dem protected Konstruktor gefällt mir ganz gut. Man lernt halt nie aus, dass man "protected" auch von "oben nach unten" anstatt von "unten nach oben" verwenden kann.
Dank+Gruss,
Martin
-
Jetzt braucht jede abgeleitete Klasse den Age Parameter, obwohl sie diesen selbst nicht braucht, sondern nur an die Basisklasse durchwinkt.
Aus Sicht des Software engineerings und der is_a Beziehung braucht Dog ist das Alter Teil des Objektes Hund. Alle Basisklasseneigenschaften durch Animal sind Teil von Dog, auch wenn diese nur "durchgereicht" werden.
Es gibt eine abstrakte Basisklasse "Base", diese benötigt zwingend einen Wert für "RequiredMember".
Tja und welcher Wert? Da das aber erst zur Laufzeit feststeht, ist ein parametrisierter Konstruktor die logische Wahl, vielleicht mit default-Wert.
Und wer bei dieser Kleinigkeit von Redundanz redet, sollte aufhoeren, objektorientiert zu arbeiten.