Laden...

OOP - Wie designt man eine Klasse mit gleichem Typ als Parent und n gleiche Typen als Kinder?

Erstellt von Larsen vor 8 Jahren Letzter Beitrag vor 8 Jahren 2.160 Views
L
Larsen Themenstarter:in
192 Beiträge seit 2006
vor 8 Jahren
OOP - Wie designt man eine Klasse mit gleichem Typ als Parent und n gleiche Typen als Kinder?

Hallo Forum,

ich habe eine Design-Frage die ich im Moment alleine nicht gelöst bekomme.

ich habe eine Object, dem ich noch eine Kategorie mitgeben möchte. Dabei spielt das Object für mein Problem gerade keine Rolle.

Die Kategorie soll eine Hauptkategorie und unterkategorien haben.
Also etwa sowas
KAT=
A-> A1;A2;A3
B-> B1;B2;B3
C-> C1;C2;C3

meinem Object möchte ich jetzt diese Kategorie in einem Property mitgeben.


public class XYZ{
    .....
    public KAT Kat {get;set;}
}

Aber wie muss die Kategorie Klasse dazu aussehen? Da die Hauptkategorien verschiedene Unterkategorien haben, weiß ich da jetzt nicht wirklich weiter.

Ich habe mir schon was "gebaut", wo die Hauptkategorie vom Type Object ist und in den get/set Anweisungen werte ich unterschiedliche Typen aus, aber da ich das Ganze auch noch nach XML serialisieren möchte kriege ich da eine Fehlermeldung.

Ich vermute, dass das irgendwie viel einfacher gehen muss und ich im Moment nur das Wald, Baum Problem habe 😃

Danke im vorraus,

Gruß
Lars

Hinweis von Coffeebean vor 8 Jahren

Ich hab auch mal den Titel angepasst. "Wie designt man sowas? " ist absolut nichtssagend und beschreibt das Problem nicht im Ansatz. Niemand kann darauf schliessen, dass der Thread hilft, wenn er ihn in der Suche findet. Wenn dir ein besserer Titel einfällt, editier ihn bitte nochmal. [Hinweis] Wie poste ich richtig? Punkt 3

...............Es gibt 10 Arten von Menschen ...............
die einen kennen binäre Zahlen, die anderen nicht!!!

16.807 Beiträge seit 2008
vor 8 Jahren

Simples OOP.

public class Category
{
   public  Category Parent {get;set;}
   public  IList<Category> Childs {get;set;}

}

Parent kann auch weg gelassen werden; wenn nicht nötig.
Ansonsten bei der Serialisierung ignorieren, damit keine Endlosschleife draus wird.

3.003 Beiträge seit 2006
vor 8 Jahren

interface ICategoryElement
{
         void SetParent(ICategoryElement parent);
         IEnumerable<ICategoryElement> Childen { get; set; }
         bool IsRoot { get; }
         void AddChild(ICategoryElement newChild);
         void RemoveChild(ICategoryElement child);
}

public class ExampleCategory 
{
         //intern mit List<ICategory> arbeiten, zB
         private List<ICategoryElement) _children = new List<ICategoryElement>();         
         private ICategoryElement _parent;

          //ctor und implementierungen einfügen...
          //Beispielimpl.
          public void AddChild(ICategoryElement newChild)
          {
                 _children.Add(newChild);
                 newChild.SetParent(this);
          }
          public bool IsRoot { get { return _parent == null; }}
}

Im Grunde, was Abt schon gesagt hat, EDIT: lesen sollte man können - allerdings mit doppelter Verkettung (jedes Kind kennt sein übergeordnetes Element). Macht sich mit einer Schnittstelle obendrauf besser, da schon abzusehen ist, dass man möglicherweise spezielle Typen von Kategories braucht.

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)

L
Larsen Themenstarter:in
192 Beiträge seit 2006
vor 8 Jahren

Hallo LaTino, Hallo Abt,

vielen Dank für eure schnelle Antwort, da ihr euch einig zu sein scheint, ist es bestimmt die richtige Lösung 😃

Abt:" Simples OOP" -> genau das ist das Problem 😃 ich komme aus der Embedded C-Welt und bin im Bereich OOP echt schlecht 😃

Also ich versuch das mal zu verstehen was ihr da geschrieben habt, im Moment ist das noch etwas unklar, aber ich komm schon dahinter 😃

Vielen DANK für den Schubs in die richtige Richtung !!!

Gruß
Lars

...............Es gibt 10 Arten von Menschen ...............
die einen kennen binäre Zahlen, die anderen nicht!!!

3.003 Beiträge seit 2006
vor 8 Jahren

Es gibt auch schon fertige Klassen innerhalb von .NET, die man dafür einfach missbrauchen könnte. Vermutlich würde ich einfach zu XDocument / XElement (sprich: eine XML-Struktur im Speicher, jeder Knoten kann Subknoten haben, du siehst die Parallelen) greifen, wenn es schnell gehen soll. Hätte als Bonus, das man die Struktur auch noch auf die Schnelle speichern und wieder laden 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)

L
Larsen Themenstarter:in
192 Beiträge seit 2006
vor 8 Jahren

Hi,

da ich euren Ansatz noch nicht ganz verstanden habe, ich bastle gerade an deinem Code in einem Testprojet herum, fällt mir das schwer einzuschätzen.

Mir geht es auch darum, die Kategorien nicht vermischen zu können. Also Wenn ich KAT->A habe möchte ich einen Fehler bekommen, wenn ich jetzt B1 als unterkategorie zuweise. Im XML oder XDocument, kann ich zwar strukturen ablegen, aber auch kunterbunt gemischt, ohne dass es auffällt.

Aber ich bin mit dem "verstehen" eures Ansatzes noch nicht durch 😃

...............Es gibt 10 Arten von Menschen ...............
die einen kennen binäre Zahlen, die anderen nicht!!!

2.298 Beiträge seit 2010
vor 8 Jahren

Mir geht es auch darum, die Kategorien nicht vermischen zu können. Also Wenn ich KAT->A habe möchte ich einen Fehler bekommen, wenn ich jetzt B1 als unterkategorie zuweise.

Genau hier kommt die Antwort von Latino zum tragen, nur in aus meiner Sicht abgewandelter Form. Ich würde es fast generisch aufbauen und den Tree entkoppelt von der eigentlichen Kategorie sehen.


public class CategoryTreeNode<T>
{
      public T Parent {get; set; }
      public List<CategoryTreeNode<T>> Children { get; set; }
}

Der Ansatz ist jetzt aber nicht zuende gedacht, sondern nur als Gedankenstütze.

Wissen ist nicht alles. Man muss es auch anwenden können.

PS Fritz!Box API - TR-064 Schnittstelle | PS EventLogManager |

3.003 Beiträge seit 2006
vor 8 Jahren

h darum, die Kategorien nicht vermischen zu können. Also Wenn ich KAT->A habe möchte ich einen Fehler bekommen, wenn ich jetzt B1 als unterkategorie zuweise. Im XML oder XDocument, kann ich zwar strukturen ablegen, aber auch kunterbunt gemischt, ohne dass es auffällt.

Richtig erkannt. An der Stelle entspricht das, was du mit einer XML-Struktur abbilden kannst, nur noch begrenzt deinen Anforderungen (man kann das mit XML-Schemata zwar heilen, aber das macht den gesparten Aufwand zunichte).

Hier fährst du dann mit dem "sauberen" Ansatz deutlich besser. Der Ansatz von inflames2k dürfte sehr flexibel sein, die Generik kann aber, wenn man nicht sehr aufpasst, einem Schwierigkeiten in Bezug auf die Klarheit der Klassenhierarchien machen. (Ich will dir davon nicht abraten, empfehle aber, Generik erst einzusetzen, wenn man den Ansatz OHNE Generik völlig verstanden hat.)

Wenn du die Zeit hast - diese Art Anforderungen sind auch eine gute Gelegenheit, sich mit einigen Entwurfsmustern vertraut zu machen und die wichtigen OOP-Konzepte in Fleisch und Blut übergehen zu lassen. Die Aha-Effekte sind schon vorprogrammiert 😉.

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)

L
Larsen Themenstarter:in
192 Beiträge seit 2006
vor 8 Jahren

@LaTino:Bei der Umsetzung in Richtung Generik stimme ich dir voll und ganz zu, da denke ich dann drüber nach, wenn ich die Typisierte variante verstanden habe.

Bei mir klemmt noch etwas bei der Überlegung, dass ich ja meine Kategorien nicht unbedingt zur Laufzeit, sonder zur Compilezeit festlegen kann.
Bei diesem Ansatz hier, baue ich je meine Kategorien zur Laufzeit zusammen.
Ich hab das auch schon soweit nachvollzogen, dass ich Objekte anlegen kann, also Kategorien und Unterkategorien. Die Frage, die sich mir stellt ist aber, was mach ich dann damit. Ich will ja hinterher meinem Object eine bestimmte Kategorie und Unterkategorie mitgeben.

Irgendwas ist in meiner Rübe noch nicht richtig sortiert 😦

Hier mein TestCode.


namespace ConsoleApplication6
{
    class Program
    {
        public interface ICategoryElement
        {
            string Name { get; set; }
            void SetParent(ICategoryElement parent);
            IEnumerable<ICategoryElement> Children { get; set; }
            bool IsRoot { get; }
            void AddChild(ICategoryElement newChild);
            //void RemoveChild(ICategoryElement child);
        }

        public class ExampleCategory : ICategoryElement
        {
            //intern mit List<ICategory> arbeiten, zB
            private List<ICategoryElement> _children = new List<ICategoryElement>();
            private ICategoryElement _parent;
            private string _Name;

            //ctor und implementierungen einfügen...
            public ExampleCategory(string Name)
            {
                _Name = Name;
            }

            //Beispielimpl.
            public void AddChild(ICategoryElement newChild)
            {
                _children.Add(newChild);
                newChild.SetParent((ICategoryElement)this);
            }
            public bool IsRoot { get { return _parent == null; } }
            public void SetParent(ICategoryElement parent)
            {
                this._parent = parent;
            }
            public IEnumerable<ICategoryElement> Children { get{return _children;} set{} }
            public string Name { get { return _Name; } set { _Name = value; } }
        }

        static void Main(string[] args)
        {
            ExampleCategory Cat1 = new ExampleCategory("Main1");
            ExampleCategory Cat11 = new ExampleCategory("Sub1");
            ExampleCategory Cat12 = new ExampleCategory("Sub2");
            ExampleCategory Cat13 = new ExampleCategory("Sub3");
            Cat1.AddChild(Cat11);
            Cat1.AddChild(Cat12);
            Cat1.AddChild(Cat13);
        }

    }
}

...............Es gibt 10 Arten von Menschen ...............
die einen kennen binäre Zahlen, die anderen nicht!!!

3.003 Beiträge seit 2006
vor 8 Jahren

Ja nun, noch kann deine Kategorie nix weiter, als einen Baum aufbauen. Jetzt noch zusätzliche Eigenschaften, die auch zu was nutze sind - Name, Wert, Navigationsziel, was auch immer.

Dann deinen eigentlichen Objekten eine Eigenschaft "Kategorie" geben und einer Kategorie zuweisen. Dann könntest du zB den hier machen:


//Liste von Datenobjekten, die eine Kategorie haben: myObjectList
var filteredObjects = myObjectList.Where(p => p.Category.Name == "Beispielkategorie");

Oder halt, was auch immer du mit den Kategorien bezwecken möchtest.

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)

W
955 Beiträge seit 2010
vor 8 Jahren

Alternativ kann man sich auch die Entwurfsmuster reinziehen, Kompositum mit Visitor und Builder

L
Larsen Themenstarter:in
192 Beiträge seit 2006
vor 8 Jahren

@witte: hab´s durch gelesen, sehr interessant.

@LaTino: Ich glaube, jetzt hab ich´s langsam kapiert. Das scheint mir sehr flexibel.

Ich hab jetzt mal ausprobiert, ob ich das serialisiert kriege: Leider gibt´s da eine Fehlermeldung

Eine nicht behandelte Ausnahme des Typs "System.InvalidOperationException" ist in System.Xml.dll aufgetreten.
Zusätzliche Informationen: Fehler beim Reflektieren des Typs 'ConsoleApplication6.Program.ExampleCategory'.


static void Main(string[] args)
        {
            ExampleCategory Cat1 = new ExampleCategory("Main1");
            ExampleCategory Cat11 = new ExampleCategory("Sub1");
            ExampleCategory Cat12 = new ExampleCategory("Sub2");
            ExampleCategory Cat13 = new ExampleCategory("Sub3");
            Cat1.AddChild(Cat11);
            Cat1.AddChild(Cat12);
            Cat1.AddChild(Cat13);

            System.Xml.Serialization.XmlSerializer x = new System.Xml.Serialization.XmlSerializer(Cat1.GetType());
            string FileName = @"..\..\..\GenPath\Test.xml";
            System.IO.Stream stream = new System.IO.FileStream(FileName, System.IO.FileMode.Create);
            x.Serialize(stream, Cat1);
            stream.Close();
        }

...............Es gibt 10 Arten von Menschen ...............
die einen kennen binäre Zahlen, die anderen nicht!!!

3.003 Beiträge seit 2006
vor 8 Jahren

Das wäre die darunterliegende Ausnahme interessanter.

Wie dem auch sei, paar generelle Anmerkungen:

  1. was witte schrieb. Dick, und fett, und unterstrichen. Insbesondere das Builder-Muster, denn dir wird selber schon aufgefallen sein, dass die Erzeugung der ganzen Kategorieobjekte irgendwie nicht so das Gelbe vom Ei ist.

  2. Alle Klassen, die IDisposable implementieren, immer mit using instanzieren:


using(var disposableObject = new DisposableObject())
{
               //tu was mit dem disposable
}

Insbesondere gilt das für Objekte, die was mit Datenverbindungen oder Zugriffen auf das Dateisystem zu tun haben, bei dir: die Variable stream. using übernimmt das Close() für dich, und zwar auch, wenn es eine Ausnahme gibt und abgebrochen wird, bevor deine Codezeile Close() kommt.
Klassen, die IDisposable implementieren, erkennst du sehr, sehr leicht an ihrer Methode Dispose().

[EDIT]Whooops, 3. vergessen 😁
Gerade bei Klassen, die du nur zu Testzwecken baust, würde ich dir empfehlen, in deinem Projektordner ein Klassenbibliotheks-Projekt anzulegen, und dort deine Klassen zu sammeln. Und im Consolen-Projekt fügst du nur eine Referenz auf diese Klassenbibliothek hinzu. Das macht das ganze viel sauberer.

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)

L
Larsen Themenstarter:in
192 Beiträge seit 2006
vor 8 Jahren

Danke für die Tips, das mit dem using... werde ich direkt an manchen stellen in meinem Code einsetzen können, das wußte ich bislang nicht.
Das mit den Testklassen kann ich auch nur bestätigen 😃 es sammelt sich ein ganz schöner Wildwuchs an 😃

Hatte übrigens vergessen zu erwähnen, dass die Fehlermeldung in dieser Zeile geworfen wird.

System.Xml.Serialization.XmlSerializer x = new System.Xml.Serialization.XmlSerializer(Cat1.GetType());

Wie dem auch sei, ich glaube mir fehlen an diversen Stellen noch viele Grundlagen. Ein Embedded C-Gehirn muss sich schon ganz schön verbiegen um in die OOP Welt zu passen 😃

Ich werde mich mal etwas mehr mit den Entwurfsmustern beschäftigen...

...............Es gibt 10 Arten von Menschen ...............
die einen kennen binäre Zahlen, die anderen nicht!!!

5.657 Beiträge seit 2006
vor 8 Jahren

Hi Larsen,

" Simples OOP" -> genau das ist das Problem 🙂 ich komme aus der Embedded C-Welt und bin im Bereich OOP echt schlecht 🙂

Wie dem auch sei, ich glaube mir fehlen an diversen Stellen noch viele Grundlagen. Ein Embedded C-Gehirn muss sich schon ganz schön verbiegen um in die OOP Welt zu passen 🙂

Dann hast du doch einen guten Ansatzpunkt, um weiterzumachen. Wenn du dich mit OOP nicht auskennst, dann kannst du auch Konzepte wie Serialisierung nicht wirklich verstehen. Ich würde empfehlen, dich erstmal mit den grundlegenden Konzepten der objektorientierten Programmierung und der Sprache C# auseinanderzusetzen. Schau mal in unsere Zusammenstellung von hilfreichen Links: [FAQ] Wie finde ich den Einstieg in C#? oder in das OpenBook vom Rheinwerk-Verlag: 🛈

Christian

Weeks of programming can save you hours of planning

L
Larsen Themenstarter:in
192 Beiträge seit 2006
vor 8 Jahren

Huch,

hab ich jetzt erst bemerkt, wer hat denn den Titel des Eintrags geändert?

Entspricht so gar nicht der ursprünglichen Fragestellung. Die Kinder sind ja nicht vom gleichen Typ wie der Parent.

Ich möchte das Thema nicht wieder aufwärmen, da schon sehr gute Antworten dabei sind. Aber die Antworten beziehen sich dadurch auf eine ganz andere Fragestellung 😦

Gruß
Lars

edit: Ach so, hab´s jetzt gesehen, die Frage war wohl nicht gut gestellt. Aber wie soll man eine Designfrage im Titel beschreiben, wenn das Problem nicht in einem Satz erklärt ist? So ist die Frage aber nicht richtig interpretiert worden.

Ich lassen den Titel jetzt so stehen, da er zu den Antworten passt.

...............Es gibt 10 Arten von Menschen ...............
die einen kennen binäre Zahlen, die anderen nicht!!!

J
251 Beiträge seit 2012
vor 8 Jahren

Ist dir der graue Abschnitt unter dem ersten Post nicht aufgefallen?

L
Larsen Themenstarter:in
192 Beiträge seit 2006
vor 8 Jahren

nein MrSparkle, erst gerade 😃

...............Es gibt 10 Arten von Menschen ...............
die einen kennen binäre Zahlen, die anderen nicht!!!

L
Larsen Themenstarter:in
192 Beiträge seit 2006
vor 8 Jahren

Hab´s mir noch mal als Verlauf angesehen, der Titel ist nachträglich, nachdem schon ein paar Antworten da waren, geändert worden.
Also ist auch meine Beschreibung des Problems so interpretiert worden 😃. Was mal wieder zeigt, dass es zwischen Sender und Empfänger von Nachrichten erhebliche Probleme geben kann, wenn der Sender sich nicht korrekt ausdrückt 😃

Werde demnächst versuchen, das klarer auf den Punkt zu bringen !!!

...............Es gibt 10 Arten von Menschen ...............
die einen kennen binäre Zahlen, die anderen nicht!!!