OOP, abgeleitete Klassen

  • VB.NET
  • .NET (FX) 4.5–4.8

Es gibt 12 Antworten in diesem Thema. Der letzte Beitrag () ist von ExcelErik.

    OOP, abgeleitete Klassen

    Hallo zusammen,

    ich lese schon seit einiger Zeit in diesem Forum mit und konnte mir dank eurer Beiträge schon sehr viel selbst beibringen. Dafür schon einmal ein Danke!

    Ich habe vor einiger Zeit nun meinen Horizont von VBA/Excel auf VB.NET erweitert. Mein erstes und derzeitiges Hauptprojekt soll ein Preis-Kalkulationsprogramm werden.
    Das Programm an sich steht auch schon seit einiger Zeit (nicht ganz fertig, aber benutzbar :D ), allerdings ohne wirkliche OOP.
    Das führt mittlerweile aber leider bei Erweiterungen/ Veränderungen des Programms zu immer mehr Krücken und Verwirrung, weil ich den Überblick verliere.
    Damit kommen wir zum Problem und hoffentlich auch der Lösung: Einarbeitung in die OOP.


    Zunächst einmal zum Verständnis die Idee / das Konzept hinter dem Projekt:

    Ist-Stand ohne OOP: Man gibt alle Parameter an, die das Produkt haben soll und muss auch die Fertigungsart (Maschinen) auswählen. Das Ergebnis sind dann u.a. die Herstellkosten.
    Im "Main"-Form sind teilweise sehr lange Funktionen / Subs, welche mit "If / Then bzw. "Select Case" die Eigenschaften etc. der angegebenen Fertigungsart und Maschine auslesen und die Produktionskosten daraufhin ermitteln.

    Problem:
    Die Anwender kennen nicht unbedingt die beste Fertigungsart, da diese sich teilweise nur in kleinen Details unterscheiden.

    Wunsch: Man gibt alle Parameter an und das Programm ermittelt die funktionierende(n) bzw. die günstigste Fertigungsart.



    Vereinfachtes Beispiel (die Sinnhaftigkeit sei mal dahingestellt):

    Ich möchte einen Würfel herstellen.
    • Eigenschaften: 6 Seiten, weiß, Kunststoff, Größe S

    • Fertigungsart1: Kann bis 6 Seiten, kann weiß, kann Kunststoff & Holz, bis Größe S
    • Fertigungsart2: Kann bis 20 Seiten, kann weiß, kann nur Holz, bis Größe L
    • Fertigungsart3: Kann bis 8 Seiten, kann alle Farben, kann nur Kunststoff, bis Größe M
    Ergebnis:
    • Fertigungsart1 und Fertigungsart3 würden in Frage kommen
    • Fertigungsart2 funktioniert aufgrund des Materials nicht.

    Die Produktionskette ist natürlich in der Realität noch wesentlich länger und tiefer, aber die folgenden Schritte würden dann ja äquivalent aufgebaut werden.
    Für die Zukunft wäre auch noch ein Wunsch, gewisse Entscheidungen vom Benutzer "überschreiben" zu lassen. So wie ich das bisher verstanden habe kann man dafür in abgeleiteten Klassen "Override" nutzen. Oder doch lieber ein einfaches "If Then Else"?




    Ich könnte mit meinem Wissensstand jetzt die Basisklasse "Würfel" erstellen, welche u.a. die Eigenschaften "Anzahl Seiten", "Farbe", "Material" und "Größe" beinhaltet.
    Aus dieser Klasse würde ich dann mit einem Konstruktor "Public Sub New(Parameter)", anhand der Eingaben im Main-Form, ein neues Objekt erstellen. So weit so gut.

    Was ist nun der sinnvollste Weg, die Einschränkungen der Fertigungsarten abzufragen und am Ende die "funktionierende(n)" Fertigungsart(en) zu ermitteln?
    1. Methoden in der Klasse "Würfel" erstellen, welche dann mit "If Then" oder "Select Case" versuchen die einzelnen Kriterien abzuarbeiten? So habe ich es momentan ja schon in meinem Programm, nur eben ohne die Klasse, sondern mit Public Variablen.
      Das ist aber, wie erwähnt, mittlerweile relativ unübersichtlich geworden.
    2. (Abgeleitete) Klassen für jede Fertigungsart erstellen, die mit den Werten aus dem erstellen Objekt gefüttert werden und dann am Ende z.B. eine eigene Property "Funktioniert" mit "True" oder "False" füllen?
    3. Eine ganz andere Vorgehensweise?

    Ich hoffe ihr versteht was ich meine und könnt mir ein paar Denkanstöße geben ?(

    Gruß Erik
    Hey,

    für die Produktionsarten brauchst Du keine Vererbung, außer ich habe Dein Problem falsch verstanden. Bilde auch jede Produktionsart in einer Klasse ab.

    VB.NET-Quellcode

    1. Public Class ProductionType
    2. Public Property MaxSides As Integer
    3. Public Property Colors As New List(Of Color)
    4. Public Property Materials As New List(Of Materials)
    5. Public Property MaxSize As Sizes
    6. Public Sub New(maxSides As Integer, colors As Color(), materials As Materials(), maxSize As Sizes)
    7. Me.MaxSides = maxSides
    8. Me.Colors.AddRange(colors)
    9. Me.Materials.AddRange(materials)
    10. Me.MaxSize = maxSize
    11. End Sub
    12. End Class


    Weiterhin legst Du für die Materialen und Großen jeweils ein ENUM an. Sollte es mit den Großen und Materialen tiefer gehen, kann man auch diese als Klasse abbilden.

    VB.NET-Quellcode

    1. Public Enum Materials
    2. Plastic = 0
    3. Wood = 1
    4. End Enum
    5. Public Enum Sizes
    6. S = 0
    7. M = 1
    8. L = 2
    9. XL = 3
    10. End Enum


    Zu Beginn Deines Programms definierst Du die verschiedenen Arten der Produktion.

    VB.NET-Quellcode

    1. Module Module1
    2. Private _productionTypes As New List(Of ProductionType)
    3. Sub Main()
    4. _productionTypes.Add(New ProductionType(6, {Color.White}, {Materials.Wood, Materials.Plastic}, Sizes.S))
    5. _productionTypes.Add(New ProductionType(20, {Color.White}, {Materials.Wood}, Sizes.L))
    6. _productionTypes.Add(New ProductionType(8, {Color.White, Color.Green, Color.Red}, {Materials.Plastic}, Sizes.M))
    7. Dim dice As New Form(6, Color.White, Materials.Plastic, Sizes.M, _productionTypes)
    8. End Sub
    9. End Module


    Ein Produktionstyp übernimmt die maximale Anzahl der Seiten, ein Array von Farben, welche dieser Typ fertigen kann, die möglichen Materialen und die maximale Größe. Als nächstes erstellst Du Dir Deine gewünschte Würfel-Instanz. Der Würfel, bzw. die Form wird als Klasse abgebildet.

    VB.NET-Quellcode

    1. Public Class Form
    2. Public Property Sides As Integer
    3. Public Property Color As Color
    4. Public Property Material As Materials
    5. Public Property Size As Sizes
    6. Public Property TypesProduction As New List(Of ProductionType)
    7. Public Sub New(sides As Integer, color As Color, material As Materials, size As Sizes, productionTypes As List(Of ProductionType))
    8. Me.Sides = sides
    9. Me.Color = color
    10. Me.Material = material
    11. Me.Size = size
    12. GetProductionType(productionTypes)
    13. End Sub
    14. Private Sub GetProductionType(productionTypes As List(Of ProductionType))
    15. For Each x As ProductionType In productionTypes
    16. If Me.Sides > x.MaxSides Then Continue For
    17. If Not x.Colors.Contains(Me.Color) Then Continue For
    18. If Not x.Materials.Contains(Me.Material) Then Continue For
    19. If Me.Size > x.MaxSize Then Continue For
    20. Me.TypesProduction.Add(x)
    21. Next
    22. End Sub
    23. End Class


    Die Klasse übernimmt die Anzahl der Seiten, die Farbe, das Material, die Größe. Ebenfalls muss der Klasse mitgeteilt werden, welche Arten der Produktion es gibt. Hier als productionTypes As List(Of ProductionType), welche beim Programmstart festgelegt wurde.

    Am Ende des Konstruktors werden die möglichen Produktionstypen ermittelt und in der Property TypesProduction gespeichert. In dieser Routine gehst Du jeden vorhandenen Produktionstypen durch und prüfst, ob die Anzahl der Seiten überschritten wurde, ob der Produktionstyp die Farbe beinhaltet, ob der Produktionstyp das Material beinhaltet und ob die Größe überschritten wurde. Übersteht ein Produktionstyp diesen Test, so wird er in der Property gespeichert, wenn nicht wird er übersprungen.

    Ich würde das in Etwa so machen. Gibt natürlich mehrere Wege, wie so oft.
    Die Unendlichkeit ist weit. Vor allem gegen Ende. ?(
    Manche Menschen sind gar nicht dumm. Sie haben nur Pech beim Denken. 8o
    Hey SpaceyX!
    Danke erstmal für die superschnelle Antwort.

    Im Grunde hast du mich schon richtig verstanden. Ich bin nur bisher immer davon ausgegangen, der Weg sei, die Klasse des Würfels in die der Produktionsarten einzuarbeiten und nicht umgekehrt.
    Ich konnte deinem Beispiel größtenteils folgen. Ich bin auch ein wenig verblüfft, da ich diese Art und Weise von alleine wahrscheinlich niemals erarbeitet hätte, sie mir vom Stil aber schon sehr gefällt.
    Leider kann ich erst Montag wieder auf das Projekt zugreifen. Bis dahin werde ich mir noch Gedanken über die Länge der Produktionskette machen bzw. wie ich diese nach deinem Beispiel bei mir abbilden könnte.
    Für meine Produkte gibt es noch diverse Weiterverarbeitungen auf weiteren Maschinen, die natürlich ebenfalls gewisse Bedingungen mitbringen.

    Gerne kannst du dir dazu auch Gedanken machen, ich versuche jedoch erstmal mich meiner eigenen grauen Zellen zu bedienen! :D
    Meine Idee dazu bisher:
    • Ebenfalls Public Properties z.B as boolean in Form einfügen, welche speichern, ob eine gewisse Weiterverarbeitung gewünscht ist oder nicht.
    • Diese dann mit If Then abfragen und bei True äquivalent zu GetProductionType() abfragen, welche der vorhandenen Maschinen (in Abhängigkeit von Stückzahl, Größe etc.) für die gewünschte Weiterverarbeitung am besten geeignet ist.
    ODER (was mir stilistisch glaube ich besser gefällt):
    • Direkt eine List(of Weiterverarbeitung) anlegen, (äquivalent zu Material und Farbe), die alle vom Benutzer angegebenen Weiterverarbeitungen beinhaltet. Hier weiß ich nur noch nicht ganz, wie ich von dort aus weitergehe.

    Dir und allen Mitlesern ein schönes Wochenende! ^^
    Versuche, Probleme getrennt zu betrachten. Beispielsweise kannst Du alle Deine Maschinen, samt Ihren Eigenschaften, als Klasse abbilden. Weiterhin kannst Du alle Produktionsschritte als Klasse abbilden und diese dann miteinander in Beziehung setzen. Vererbung macht z. B. bei Maschinen oder den verschiedenen Produkten Sinn. Melde Dich wieder, wenn Du nicht weiter kommst, bzw. konkrete Daten zu dem Problem hast.
    Die Unendlichkeit ist weit. Vor allem gegen Ende. ?(
    Manche Menschen sind gar nicht dumm. Sie haben nur Pech beim Denken. 8o
    Hallo nochmal,

    ich habe leider momentan nicht so viel Zeit an meinem Projekt zu arbeiten, aber ich bin schon eine ganze Ecke weitergekommen.


    Ich habe momentan einige Boolean-Eigenschaften für meine Produkte. Also ganz simpel "Eigenschaft vorhanden", "nicht vorhanden".
    Jetzt würde ich diese aber gerne erweitern, da als weitere Dimension ein Zeitpunkt wichtig wäre. Also eine Eigenschaft ist z.B. vorhanden und wird vor Arbeitsschritt X hinzugefügt oder nach Arbeitsschritt X.

    Macht man dafür am besten eine Enumeration mit:

    VB.NET-Quellcode

    1. Public Enum Eigenschaft_XY
    2. nicht_vorhanden = 0
    3. vorhanden_vorher = 1
    4. vorhanden_nachher = 2
    5. End Enum


    oder gibt es da andere Vorgehensweisen?
    @ExcelErik Es kommt darauf an, wie Du das verwenden willst.
    Ein Enum ist eine Aufzählung von Zuständen, von der genau ein Zustand aktiv ist (etwas anders ist da ein Flag-Enum).
    Wenn sich der Gebrauch mit einer solchen Aufzählung gut bearbeiten lässt, dann nimm ein Enum.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Flag-Enum war mir bisher nicht bekannt, aber so wie ich das jetzt verstehe können dann mehrere Zustände des Enums zutreffen? Quasi wie der Unterschied zwischen Checkbox und Radiobutton.
    Da es sich bei mir im Grunde immer noch um eine Art boolean handelt werde ich wohl beim normalen Enum bleiben, da sich alle Ausprägungen voneinander ausschließen.

    Mein Plan ist, z.B. für die Eigenschaft "X", anzugeben, ob und wenn ja, wann diese in der Produktionskette bearbeitet wird. Die Berechnung dieser Eigenschaft "X" verändert sich in Abhängigkeit des Zeitpunktes.
    Wenn der Zeitpunkt "vorher" gewählt wird, dann ist das Grundmaterial z.B. noch nicht geschnitten und ich kann den Schritt auf das gesamte Grundmaterial rechnen. Beim Zeitpunkt "nachher" habe ich das Material bereits geschnitten und muss den Prozess für jedes einzelne geschnittene Teil wiederholen. Ob der erste oder der zweite Fall sinnvoller ist, hängt wiederum von einigen anderen Parametern ab. Ebenso entscheidet das "vorher / nachher" z.B. darüber mit welcher Maschine geschnitten wird.
    Ich hoffe das ist für Außenstehende verständlich :/

    Danke jedenfalls!

    ExcelErik schrieb:

    Quasi wie der Unterschied zwischen Checkbox und Radiobutton.
    Nicht ganz.
    Mehrere RadioButton bilden ein Enum ab.
    Mehrere CheckBoxen bilden ein Flag-Enum ab.
    ====
    Für Dein Problem eicht m.E. ein Boolean, das vorher/nachher unterscheidet. Einen dritten Weg gibt es da wohl nicht.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!

    RodFromGermany schrieb:

    Zitat von ExcelErik: „Quasi wie der Unterschied zwischen Checkbox und Radiobutton.“Nicht ganz.
    Mehrere RadioButton bilden ein Enum ab.
    Mehrere CheckBoxen bilden ein Flag-Enum ab.


    Ja, das meinte ich damit. Ein einzelner Button / eine einzelne Box macht in diesem Vergleich auch keinen Sinn :D

    RodFromGermany schrieb:


    Für Dein Problem eicht m.E. ein Boolean, das vorher/nachher unterscheidet. Einen dritten Weg gibt es da wohl nicht.


    Aber es gibt ja auch noch die Möglichkeit, dass diese Eigenschaft bei einem Produkt gar nicht vorhanden ist. Demnach bräuchte ich doch dann eine boolean Property vorhanden/ nicht vorhanden und eine weitere für vorher / nachher, wobei in diesem Fall auch die Kombination von "nicht vorhanden" und "vorher" oder "nachher" exisitieren kann, was aber doch unlogisch wäre. Vielleicht ist aber auch mein ganzes Klassendesign für die Tonne ?( . Ich werde es aber erstmal mit dem Enum versuchen umzusetzen.

    ExcelErik schrieb:

    dass diese Eigenschaft bei einem Produkt gar nicht vorhanden ist
    Probierma, das als Instanz zu sehen und die Instanz bei nicht vorhandener Eigenschaft gar nicht erst anzulegen.
    Das ist eine andere Philosophie, sie kommt aber sehr gut mit einem komplexen Ablaufplan zurecht.
    Alle Schritte werden als Klasse mit einer gemeinsamen Basisklasse beschrieben.
    Der Ablauf kommt dann als Instanz der jeweiligen abgeleiteten Klasse in eine List(Of BASISKLASSE), deren Member dann elementweise abgearbeitet werden.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Diese Vorgehensweise habe ich so für die einzelnen Maschinen eingebaut.
    Die Maschinen werden mit ihren Properties in einer List(of T) gespeichert, sofern der Benutzer sie ausgewählt hat und die Filterkriterien für die Maschine alle erfüllt sind.
    Für diese Kriterien brauche ich aber wiederum natürlich das "vorhanden / nicht vorhanden"-sein von den verschiedenen Eigenschaften des Produktes.
    Bei deinem Vorschlag müsste ich dann die Eigenschaften für die Kriterien der Maschinen, durch auslesen der Member der List(of Eigenschaft) ermitteln, statt aus direkt zugreifbaren Eigenschaften in der Klasse.

    Ich frage mich, ob die Methode in meinem Fall dann wirklich besser ist.
    Oder würdest du es so machen, dass die jeweilige benutzte Maschine (mit ihren Properties wie z.B. Kosten, Zeiten, Restriktionen,) nur eine Property der Klasse "Eigenschaft" ist?
    Momentan hätte ich sonst zwei List(of T), eine für die verwendeten Maschinen und eine für die Eigenschaften.

    Ich hoffe ich würfel hier jetzt nichts durcheinander :S
    @ExcelErik Ich hoffe, dass wir uns jetzt richtig verstehen.
    Ohne mich jetzt bis ins letzte Detail mit dem Datenmodell vertraut zu machen, würde ich das wohl jetzt so machen:
    Keine List(Of T), sondern ein Dictionary(Of ENUM, T).
    Das ENUM listet alle verfügbaren Eigenschaften auf. Ist eine Eigenschaft nicht vorhanden, steht neben dem ENUM-Value ein Nothing, ansonsten die Daten-Instanz.
    Jeder abarbeitende Prozess kann nun sieses Dictionary abfragen, ob "sein" ENUM-Value hinterlegt ist oder nicht.
    Für die Protokollierung ist es übersichtlicher, wenn die Möglichkeit vorhanden, aber nicht hinterlegt ist, außerdem kann nachträglich ganz elegant eine Daten-Instanz hinzugefügt werdfen.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!