Laden...

Design: Property vom Typ mehrerer möglicher Klassen

Erstellt von dmdba vor 14 Jahren Letzter Beitrag vor 14 Jahren 1.580 Views
D
dmdba Themenstarter:in
6 Beiträge seit 2008
vor 14 Jahren
Design: Property vom Typ mehrerer möglicher Klassen

Hallo,

ich weiß nicht wie ich das im Titel hätte besser beschreiben können.

Ich habe folgendes Problem:

Angenommen ich habe folgende Objekte


public interface IBasisFahrzeug
{
    int Reifen { get; set; }
    int Tueren { get; set; }
}

public class PKW : IBasisFahrzeug
{
    public int Reifen { get; set; }
    public int Tueren { get; set; }

    public string Farbe { get; set; }
}

public class LKW: IBasisFahrzeug
{
    public int Reifen { get; set; }
    public int Tueren { get; set; }

    public string Ladung { get; set; }
}

public class Bestellung
{
    public IBasisFahrzeug Fahrzeug { get; set; }
}

Wenn ich nun so ein Bestellungsobjekt erstelle:


Bestellung b = new Bestellung();
b.Fahrzeug = new PKW();
b.Fahrzeug.Reifen = 4;
b.Fahrzeug.Tueren = 3;

b.Fahrzeug.Farbe = "rot"; // Geht dies hier nicht!

Es gehen natürlich alle Properties die auch im Interface deklariert wurden. Weitere Properties die ich den Klassen, die von dem Interface erben, erstelle sind nicht "erreichbar".

Wenn ich wirklich nur ein Property für das Fahrzeug in der Bestellung haben möchte und keine Properties die ich je nachdem ob es ein PKW oder LKW ist hernehme... Wie kann ich das machen.

Wäre für Hilfe sehr dankbar...

3.003 Beiträge seit 2006
vor 14 Jahren

Wenn dein Property vom Typ IFahrzeug sein soll, wirst du keine Properties abfragen können, die in dieser Schnittstelle nicht definiert sind - nicht, ohne zu wissen, was für ein Objekt du nun genau da drin hast. Und wenn du darauf angewiesen bist, genau zu wissen, was für ein Objekt du da drin hast, dann wäre die Schnittstelle wiederum ziemlich sinnlos:


PKW pkw = bestellung.Fahrzeug as PKW;
if(pkw != null) Console.WriteLine("{0}", pkw.Farbe);

LKW lkw = bestellung.Fahrzeug as LKW;
if(lkw != null) Console.WriteLine("{0}", lkw.Ladung);

Die Frage ist, was du durch die Objektvererbung eigentlich erreichen willst. Der Ansatz wirkt ein bisschen seltsam.

LaTino

"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)

D
dmdba Themenstarter:in
6 Beiträge seit 2008
vor 14 Jahren

Na am liebsten wäre mir eigentlich eine Basis Klasse mit Eigenschaften, die beide LKW und PKW teilen. und dann erben die beiden davon und haben dann noch eigene Eigenschaften. Aber da weiß ich erste recht nicht wie ich dann das in eine einzige Eigenschaft in der Bestellung rein bringe....

Ich möchte einfach in die Bestellung ein PKW oder LKW einfügen können. Und das in ein einziges Property "Fahrzeug", da zb nachher noch Motoräder oder anderes hinzukommen und ich nicht in der Bestellung pro Fahrzeugart ein eigenes Property haben will.

4.931 Beiträge seit 2008
vor 14 Jahren

Du mußt zuerst ein konkrete Objekt erstellen und es dann der Bestellung zuweisen:


Bestellung b = new Bestellung();

PKW pkw = new PKW();
pkw.Reifen = 4;
pkw.Tueren = 3;
pkw.Farbe = "rot"; // <- funktioniert nun, da PKW eine Farbe hat

b.Fahrzeug = pkw;

Und zum Auslesen kannst du dann den Code von LaTino verwenden.

P.S. Warum haben LKWs denn keine Farbe?

3.003 Beiträge seit 2006
vor 14 Jahren

Du mußt zuerst ein konkrete Objekt erstellen und es dann der Bestellung zuweisen

Idealerweise weiss man weder beim Erstellen noch beim Auslesen den konkreten Typ des Objekts, das da erstellt/ausgelesen wird. Dafür gibt's ja eben die Schnittstelle. Und das ist auch der Grund, wieso mir das Konstrukt seltsam vorkommt.

LaTino

"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)

D
dmdba Themenstarter:in
6 Beiträge seit 2008
vor 14 Jahren

Idealerweise weiss man weder beim Erstellen noch beim Auslesen den konkreten Typ des Objekts, das da erstellt/ausgelesen wird

Aber wie ist es denn dann am besten zu lösen?

Ich will einfach, dass ich der Bestellung ein Fahrzeug übergeben kann. Unabhängig was es nun konkret für eines ist. Und diese können sich in den Eigenschaften eben auch teilweise unterscheiden.

Ich weiß einfach nicht wie man sowas allgemein löst.

4.931 Beiträge seit 2008
vor 14 Jahren

Hä???

LaTino, was willst du mir (uns) damit sagen?

Irgendein konkretes Objekt muß es schon geben - man kann schließlich keine Programme schreiben, die nur aus Interfaces bestehen.

Und an dmdba:
deine Klassenhierarchie ist schon korrekt so und wenn du die Erstellung der konkreten Objekte noch in einzelne Methoden bzw. eine sog. Fabrikmethode auslagerst, dann wird alles gut...

3.003 Beiträge seit 2006
vor 14 Jahren

@th69: Ich will damit ausdrücken, dass dein Vorschlag darauf hinausläuft, dass du ihn nicht gegen ein Interface, sondern gegen konkrete Typen programmieren lassen willst (scheint zumindest so). Und ich bestreite aufs heftigste, dass Clients irgendwo irgendwelche konkreten Typen kennen müssen. Eher im Gegenteil. Ich will meine Bibliotheken erweitern können, ohne meine Clients umzuschreiben.

Das Problem mit dem Casten ist - sobald du eine neue Fahrzeugklasse einführst, musst du auch die Anwendung dieser Klassen ändern (also die Stelle, an der bestellung.Fahrzeug aufgerufen wird). Und das ist nicht das Ziel.

Wenn deine Anwendung uU auf Farbe oder Last zugreifen muss, ohne die konkreten Typen zu kennen, dann wird dir nichts weiter übrig bleiben, als auch die Eigenschaften ins Interface aufzunehmen, die keine Implementation in einigen der konkreten Typen haben. Eine Umgestaltung als abstrakte Oberklasse Fahrzeug mit vorgegebenen Standardverhalten zum Beispiel. Oder eine Kombination:


interface IFahrzeug {...}
interface IPaintable { string Farbe { get; set; } }
interface ITransporting { string Ladung { get; set; } }

...

IPaintable fahrzeug = bestellung.Fahrzeug as IPaintable;
if(fahrzeug != null) Console.WriteLine("{0}", fahrzeug.Farbe);

Möglichkeiten gibt's viele. Wie sinnvoll welche ist, hängt von deiner Klassenstruktur ab.

Du erkennst das Muster? Dein Ziel muss sein, dass du jederzeit neue Klassen einführen kannst, OHNE irgendwas an deinem Client (also der anwendung, die mit den Bestellungen hantiert) ändern zu müssen.

Hoffe, das war verständlich.

LaTino

"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)

4.931 Beiträge seit 2008
vor 14 Jahren

Es ging mir bei meinem Source-Code doch nur um die (konkrete) Erstellung eines Fahrzeugs und dann die Zuweisung an die Bestellung (damit der Source-Code überhaupt kompiliert).

Dein Ansatz, generell über Interfaces zu gehen, ist sicherlich der beste Weg.
Jedoch wirst auch du den Client-Code ändern müssen, wenn ein neuer Fahrzeugtyp (z.B. Wasserfahrzeug mit eigenen Properties - egal ob als Interface oder aber konkrete Klasse implementiert) hinzugefügt wird.

Vllt. sollte 'dmdba' mal selber sagen, um was für ein Projekt es sich handelt.
Iich gehe mal von einer Aufgabe aus - und hoffentlich nicht von Produktionscode, denn dann sollte man schon über die Grundlagen von C# bzw. Software-Engeneering verfügen!!!).

3.003 Beiträge seit 2006
vor 14 Jahren

Jedoch wirst auch du den Client-Code ändern müssen, wenn ein neuer Fahrzeugtyp (z.B. Wasserfahrzeug mit eigenen Properties - egal ob als Interface oder aber konkrete Klasse implementiert) hinzugefügt wird.


class JamesBondsDream : IFahrzeug, IPaintable, IFloatable {..}

Solang der Client nicht auf Eigenschaften von IFloatable zugreifen will, muss er nicht verändert werden, und dennoch kann ein Wasserfahrzeug in eine Bestellung eingefügt werden. Das meine ich. Geht aber bisschen weit weg von der ursprünglichen Frage, und über die multiplen Interfaceangaben bin ich selbst nicht sehr glücklich - wie gesagt, ist nur ein Weg - aber der, mit dem man den Ansatz am schnellsten veranschaulicht.

LaTino

"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)

2.760 Beiträge seit 2006
vor 14 Jahren

Jedoch wirst auch du den Client-Code ändern müssen, wenn ein neuer Fahrzeugtyp (z.B. Wasserfahrzeug mit eigenen Properties - egal ob als Interface oder aber konkrete Klasse implementiert) hinzugefügt wird.

Nein, nicht zwangsläufig. Es gibt ja schließlich den PropertyDescriptor, Reflection und das PropertyGrid.

Vllt. sollte 'dmdba' mal selber sagen, um was für ein Projekt es sich handelt.

Jo, vor allem was er mit den Objekten nach deren Erzeugung alles anstellen möchte.

5.941 Beiträge seit 2005
vor 14 Jahren

Hallo jaensen

Nein, nicht zwangsläufig. Es gibt ja schließlich den PropertyDescriptor, Reflection und das PropertyGrid.

Das sind aber alles Möglichkeiten, die man nur nutzen sollte, wenn es nicht anders geht und es nötig ist.

Man kann sehr viel aufbauen, ohne von diesen Möglichkeiten Gebrauch zu machen.
Früher gabs ja schliesslich auch keine Reflection 😉.

Für normalen Code, ausgenommen speziellere Dinge wie einmalige Registrierung von Typen über die Reflection (Also dann keine Laufzeit Reflection mehr) sollte Reflection wirklich nur verwendet werden, wenn es geht.

Man kan halt sehr viel Murks damit machen, ein schlechtes Design geradebiegen oder sogar die Kapselung sinnlos durchbrechen.
Auch wenn es da natürlich auch Möglichkeiten gibt, um das sinnvoll anzuwenden, wie bspw. Serialisierung.

Obwohl es auch wiederum dafür Design Patterns gibt 😃

Mit einem Hammer kann man Nägel einschlagen (Sinnvolle Nutzung) oder auch damit Klavier spielen (Nicht sinnvolle Nutzung).
Wieso den Hammer nehmen, wenn es auch mit den Händen geht? 😃

Gruss Peter

--
Microsoft MVP - Visual Developer ASP / ASP.NET, Switzerland 2007 - 2011

2.760 Beiträge seit 2006
vor 14 Jahren

Natürlich nicht immer die erste Wahl sollte diese Beipielhafte Auswahl nur zeigen das man die Daten immer Angezeigt bzw. Editiert bekommt.

Das sind aber alles Möglichkeiten, die man nur nutzen sollte, wenn es nicht anders geht und es nötig ist.

Im Fall des TypeDescriptionProvider würde ich das jetzt allerdings nicht so stehen lassen da dieser gerade in der GUI durchaus einiges an flexibilität ermöglicht und auch seitens MS intensiv verwendung findet.

Früher gabs ja schliesslich auch keine Reflection 😉.

Deswegen erfanden die Leute so sachen wie die GLib (o.k. gibts auch noch nicht so lange)...

5.941 Beiträge seit 2005
vor 14 Jahren

Hallo jaensen

Ja, aber das sind alles sinnvolle Anwendungen.
Wenn es irgendwie anders geht, benutzt das auch MS nicht 😃.

Gruss Peter

--
Microsoft MVP - Visual Developer ASP / ASP.NET, Switzerland 2007 - 2011

2.760 Beiträge seit 2006
vor 14 Jahren

wer schreibt schon sinnlose Anwendungen 😁

Ein möglicher Ansatz (nur für Daten) wäre hier: [gelöst] Assembly enhancen und dynamisch laden...? beschrieben (eigentlich nur composition).
Da brauchts dann kein Reflection aber man kann sich trotzdem beliebige Datenstrukturen zusammenbasteln. Natürlich ist das Konzept noch stark erweiterbar (Sachen weiter verschachteln und so Zeugl)...

5.941 Beiträge seit 2005
vor 14 Jahren

Salute jaensen

wer schreibt schon sinnlose Anwendungen 😄

Hehe, ich habe natürlich nicht Anwendungen im Sinne von "Applikationen" gemeint, sondern im Sinne von "anwenden", die "Anwendung" einer Technik 😃.

Gruss Peter

--
Microsoft MVP - Visual Developer ASP / ASP.NET, Switzerland 2007 - 2011

C
401 Beiträge seit 2007
vor 14 Jahren

Hmm.. wie wäre es mit einer Factory?

Bsp:



public static class BestellungFactory
{
    public static IBestellung bestellungForPKW(...)
    {
        IFahrzeug pkw = FahrzeugFactory.getPKW(...);
        return new Bestellung(pkw);
    }
}


Und wenn du auf die Properties zugreifen willst kannst ja auch noch auf den konkreten Typen casten.

3.003 Beiträge seit 2006
vor 14 Jahren

Und wenn du auf die Properties zugreifen willst kannst ja auch noch auf den konkreten Typen casten.

Womit du schön genau das zusammengefasst hast, was hier überlegt wurde, wie man es vermeiden kann 😁

LaTino

"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)