none
Fragen zum Klassenentwurf RRS feed

  • Frage

  • Hallo,

    ich lese gerade das Buch "Visual Basic 2008 - Das Entwicklerbuch" von Löffelmann und habe zum Klassenentwurf einige Fragen an die Fachleute unter Euch. Im Kapitel "Vererben von Klassen und Polymorphie" hat der Author eine Basisklasse ShopItem und eine davon abgeleitete Klasse BookItem erstellt, wie im folgendem Listing zu sehen:

    Public Class ShopItem
      Public Enum PrintType
        Brief
        Detailed
      End Enum
    
      '...
      Protected myNetPrice As Double
      Protected myPrintTypeSetting As PrintType
    
      Public Sub New()
        myPrintTypeSetting = PrintType.Detailed
      End Sub
    
      Public Sub New(ByVal StringArray() As String)
        '...
        PrintTypeSetting = PrintType.Detailed
      End Sub
    
      Public Property PrintTypeSetting() As PrintType
        Get
          Return myPrintTypeSetting
        End Get
        Set(ByVal value As PrintType)
          myPrintTypeSetting = value
        End Set
      End Property
    
      Public Property NetPrice() As Double
        Get
          Return myNetPrice
        End Get
        Set(ByVal value As Double)
          myNetPrice = value
        End Set
      End Property
    
      Public Overridable Property GrossPrice() As Double
        Get
          Return myNetPrice * 1.19
        End Get
        Set(ByVal value As Double)
          myNetPrice = value / 1.19
        End Set
      End Property
    
      '...
    End Class
    
    Public Class BookItem : Inherits ShopItem
      '...
    
      Public Overrides Property GrossPrice() As Double
        Get
          Return myNetPrice * 1.07
        End Get
        Set(ByVal value As Double)
          myNetPrice = value / 1.07
        End Set
      End Property
    
      '...
    End Class
    

    Und dazu habe ich einige Verständnisfragen:

    1. In der Basisklasse weist der Author im ersten Konstruktor der Member-Variablen myPrintTypeSetting den Wert direkt zu, im zweiten Konstruktor hingegen verwendet er die Eigenschaft PrintTypeSetting. Wann sollte man im Konstruktor eine direkte Variablenzuweisung verwenden und wann eine Zuweisung über die Eigenschaft?

    2. In der Basisklasse werden die Member-Variablen als Protected definiert, um laut Author in der vererbenden Klasse auf diese Variablen direkt zugreifen zu können. Er hat in der Eigenschaft GrossPrice also myNetPrice direkt verwendet. Man kann aber auch über die Eigenschaft NetPrice darauf zugreifen (s. unteres Listing). Warum also dann Member-Variablen in der Basisklasse als Protected definieren?

    Public Class BookItem : Inherits ShopItem
      '...
    
      Public Overrides Property GrossPrice() As Double
        Get
          Return NetPrice * 1.07
        End Get
        Set(ByVal value As Double)
          NetPrice = value / 1.07
        End Set
      End Property
    
      '...
    End Class
    
    Vielen Dank für Eure Antworten im Voraus.
    Gruß LittleBlueBird
    Freitag, 30. Juli 2010 09:06

Antworten

  • Hallo LittleBlueBird,

    Ein generelles Problem bei solchen Einführungsbüchern ist,
    dass die Autoren viel auf knappen Raum vorstellen wollen/müssen.
    Dabei kommt schon mal das eine oder andere durcheinander oder
    es wird abgekürzt. Und so sollte man sich vorsichtig sein, sich an
    dem Design dort vorgestellter Klassen zu orientieren.

     

    Zu 2.) Protected vs. Eigenschaft:

    Wenn man eine Eigenschaft definiert hat, sollten das (oder die) damit
    verbundene Felder privat deklariert werden. Damit wird sichergestellt,
    dass nur ein Zugriffspfad existiert. Ansonsten besteht die Gefahr,
    sollte die Eigenschaft geändert werden, dass sich abgeleitete Klassen
    nicht mehr entsprechend verhalten.

    Wenn überhaupt sollte Protected nur eingesetzt werden, wenn Klassen
    sehr eng gekoppelt, so dass eine Änderung an der einen Klasse eine
    Änderung an der anderen zwangsläufig nach sich zieht.
    Bei Public Klassen sollte  Protected Friend verwendet werden
    (sonst findet sich immer einer, der eine Abkürzung gehen will ;-)

    Hinweis am Rande:
    Mit VB 2010 kennt auch Visual Basic automatische Eigenschaften ,
    wo man die Felder (Variablen) erst gar nicht mehr zu sehen bekommt.

     

    Zu 1.) Zuweisung an Feld vs Eigenschaft:

    Wenn man 2.) berücksichtigt, wäre das in abgeleiteten Klassen kein Thema mehr,
    da dort nur die Eigenschaft zur Verfügung steht.

    Was die Basisklasse angeht, gehen die Meinungen schon mal auseinander -
    dem einen oder anderen ist der (etwas schnellere) Feldzugriff wichtig.
    Ich würde aber auch dort i. a. Werte über die Eigenschaften setzen.

    So wird es auch leichter, wenn man weitere Dinge implementieren muß,
    wie z. B. für Datenbindungen erforderliche Schnittstellen wie INotifyPropertyChanged.

     

    Einige weitere Design-Sünden - der Autor wollte/mußte hier wohl Platz sparen
    und sei damit entschuldigt:

    Die Konstruktoren mit einem String-Array sind mehr als zweifelhaft -
    da hilft auch die Enumeration nichts mehr ;-)
    Zum einen muß der Programmierer mühsam ein Array erstellen,
    wo er keine Hinweise über die Zusammensetzung erhält

    Zudem müsste im Konstruktor geprüft, ob das Array überhaupt vorhanden ist (nicht Nothing)
    und die Zahl der Parameter überhaupt ausreichend sind und die Datentypen  passen -
    sonst kommt es zu einer unkontrollierten Ausnahme.

    Will man für die einfache Anlage einen Konstruktor mit Parametern anbieten,
    so sollte man sich der Mühe unterziehen und jeden Parameter (mit dem
    entsprechenen Typ) vorzugeben. Dann kann der Programmierer anhand
    von Intellisense die Paramter zuordnen.

     

    Praktisch würde man die Mehrwertsteuersätze direkt in eine Eigenschaft
    einbauen und schon gar nicht direkt über die Zahlenwerte (magic number ).
    Absolutes Minimum wäre hier eine Eigenschaft oder Konstante.
    Am sinnvollsten aber eine eigene Klasse Umsatzsteuer, um eine zentrale
    Stelle zu haben, die bei Änderungen betroffen ist -
    denn sollte unserem Finanzminister mal wieder das Geld zu knapp werden
    ändert sich der Steuersatz schneller als man gucken kann ;-)

    Und dann hätte jedes ShopItem eine Eigenschaft Umsatzsteuer
    und eine Überladung für GrossPrice wäre hinfällig.

     

    Ein weiterere (potentielle[1]) Schwachstelle wären die formatierten Werte.
    Da dort keine Culture übergeben wird, wird die aktuelle Kultur des Threads verwendet.
    Was nicht immer das sein wird, was man formattiert sehen möchte,
    mehr siehe Codierung und Lokalisierung

    Für das angehängte "Euro" gilt das oben zu Mehrwertsteuer Gesagte.

     

    Zum Schluß: Die in dem Bereich vorgestellte Klasse DynamicList sollte man
    über eine List(Of T) oder eine Collection(Of T) implementieren.

     

    Das sind nur einige Punkte die mir beim Überfliegen aufgefallen sind.
    Woraus Du jetzt aber auch nicht schließen solltest, dass man das Buch
    gleich wegwerfen sollte ;-) Vielmehr das Eingangs gesagte berücksichtigen,
    und für OOP einen weiteren Titel suchen, der sich damit beschäfttigt.
    Und da es wenige gibt, die direkt für Visual Basic geschrieben sind,
    kann man die zuvor erworbenenen VB Kenntnisse darauf anwenden.

    Gruß Elmar

    [1] Wird es nie als mehr als ein Minishop mag man das ignorieren ;-)

    Freitag, 30. Juli 2010 10:28
  •  
    Zur Antwort auf die zweite Frage hätte ich noch eine Anmerkung. In der Eigenschaft NetPrice findet keine �?berprüfung der Werte statt, daher kann man ohne Bedenken in der Basisklasse die Variable myNetPrice als Private deklarieren und in der abgeleiteten Klasse in GrossPrice die Eigenschaft NetPrice verwenden.
    Dass keine �?berprüfung stattfindet ist ein derzeitiger Entwicklungsstand. Stell Dir vor, in einiger Zeit wird in der Basisklasse zusätzlicher Code eingebaut, da sich beispielsweise die Anforderungen verändern/verschärfen. Wegen der Kapselung, auf die sich der Entwickler erst einmal verlässt, werden ggf. nicht alle anderen Klassen, z.B. die erbenden Klassen überprüft, ob da etwas nicht mehr passt. Das Ergebnis eines Zugriffs direkt auf die interne (protected) Variablen bewirkt, dass der neu hinzugefügte Code bei Nutzung der erbenden Klasse nicht ausgeführt wird. Ob das so gewollt ist, wird vielleicht erst beim Anwender erkannt. Jedenfalls entspricht die Nutzung geschützter (protected) Variablen in erbenden Klassen anstelle der dazugehörenden Eigenschaften keiner fehlertoleranten Programmierung. In C# ist das Problem noch schärfer, da dort zwischen Gro�?- und Kleinschreibung unterschieden wird und häufig die interne Variable und Eigenschaftsbezeichnung nur durch Gro�?- und Kleinschreibung eines einzelne Buchstabens unterscheiden.
     

    --
    Viele Gruesse
    Peter

    Freitag, 30. Juli 2010 12:06

Alle Antworten

  •  

    1. In der Basisklasse weist der Author im ersten Konstruktor der Member-Variablen myPrintTypeSetting den Wert direkt zu, im zweiten Konstruktor hingegen verwendet er die Eigenschaft PrintTypeSetting. Wann sollte man im Konstruktor eine direkte Variablenzuweisung verwenden und wann eine Zuweisung über die Eigenschaft?

    Solange die Eigenschaft nur zum ausschlie�?lichen Puffern der Daten genutzt wird, ist es egal. Wenn die Eigenschaft zusätzlichen Code enthält (z.B. Prüfung der Gültigkeit des zugewiesenen Eigenschaftswertes), dann sollte man den Weg über die Eigenschaft wählen. Da zu Beginn der Entwurfsarbeiten u.U. noch nicht klar ist, ob später noch eine Prüfung eingebaut wird, sollte man bereits am Anfang die Zuweisung über die Eigenschaft wählen. Wenn jedoch der Eigenschaftcode einen gro�?en Aufwand bedeutet (z.B. Nachfrage in einer Datenbank), dann sollte man genau prüfen, welcher Weg besser ist und ggf. vorerst den sicheren Weg anwenden und später ggf. in einer Revision den Code durch direkten Zugriff auf die internen Variablen beschleunigen.

    2. In der Basisklasse werden die Member-Variablen als Protected definiert, um laut Author in der vererbenden Klasse auf diese Variablen direkt zugreifen zu können. Er hat in der Eigenschaft GrossPrice also myNetPrice direkt verwendet. Man kann aber auch über die Eigenschaft NetPrice darauf zugreifen (s. unteres Listing). Warum also dann Member-Variablen in der Basisklasse als Protected definieren?

    Auch hier gilt wieder das oben Genannte zzgl. weiterer zu beachtender Bedingungen. Wenn die �?berschreibung eigenen Code nutzt (z.B. andere �?berprüfung der Eigenschaftswerte), die Code in der Basisklasse überflüssig macth oder sogar diesem Code widerspricht, dann sollte natürlich der Zugriff auf die internen Variablen genutzt werden.

    Falls Du Fragen zum Thema "Polymorphie" hast, dann melde Dich nochmals.

    --
    Viele Gruesse
    Peter

    Freitag, 30. Juli 2010 10:11
  • Hallo LittleBlueBird,

    Ein generelles Problem bei solchen Einführungsbüchern ist,
    dass die Autoren viel auf knappen Raum vorstellen wollen/müssen.
    Dabei kommt schon mal das eine oder andere durcheinander oder
    es wird abgekürzt. Und so sollte man sich vorsichtig sein, sich an
    dem Design dort vorgestellter Klassen zu orientieren.

     

    Zu 2.) Protected vs. Eigenschaft:

    Wenn man eine Eigenschaft definiert hat, sollten das (oder die) damit
    verbundene Felder privat deklariert werden. Damit wird sichergestellt,
    dass nur ein Zugriffspfad existiert. Ansonsten besteht die Gefahr,
    sollte die Eigenschaft geändert werden, dass sich abgeleitete Klassen
    nicht mehr entsprechend verhalten.

    Wenn überhaupt sollte Protected nur eingesetzt werden, wenn Klassen
    sehr eng gekoppelt, so dass eine Änderung an der einen Klasse eine
    Änderung an der anderen zwangsläufig nach sich zieht.
    Bei Public Klassen sollte  Protected Friend verwendet werden
    (sonst findet sich immer einer, der eine Abkürzung gehen will ;-)

    Hinweis am Rande:
    Mit VB 2010 kennt auch Visual Basic automatische Eigenschaften ,
    wo man die Felder (Variablen) erst gar nicht mehr zu sehen bekommt.

     

    Zu 1.) Zuweisung an Feld vs Eigenschaft:

    Wenn man 2.) berücksichtigt, wäre das in abgeleiteten Klassen kein Thema mehr,
    da dort nur die Eigenschaft zur Verfügung steht.

    Was die Basisklasse angeht, gehen die Meinungen schon mal auseinander -
    dem einen oder anderen ist der (etwas schnellere) Feldzugriff wichtig.
    Ich würde aber auch dort i. a. Werte über die Eigenschaften setzen.

    So wird es auch leichter, wenn man weitere Dinge implementieren muß,
    wie z. B. für Datenbindungen erforderliche Schnittstellen wie INotifyPropertyChanged.

     

    Einige weitere Design-Sünden - der Autor wollte/mußte hier wohl Platz sparen
    und sei damit entschuldigt:

    Die Konstruktoren mit einem String-Array sind mehr als zweifelhaft -
    da hilft auch die Enumeration nichts mehr ;-)
    Zum einen muß der Programmierer mühsam ein Array erstellen,
    wo er keine Hinweise über die Zusammensetzung erhält

    Zudem müsste im Konstruktor geprüft, ob das Array überhaupt vorhanden ist (nicht Nothing)
    und die Zahl der Parameter überhaupt ausreichend sind und die Datentypen  passen -
    sonst kommt es zu einer unkontrollierten Ausnahme.

    Will man für die einfache Anlage einen Konstruktor mit Parametern anbieten,
    so sollte man sich der Mühe unterziehen und jeden Parameter (mit dem
    entsprechenen Typ) vorzugeben. Dann kann der Programmierer anhand
    von Intellisense die Paramter zuordnen.

     

    Praktisch würde man die Mehrwertsteuersätze direkt in eine Eigenschaft
    einbauen und schon gar nicht direkt über die Zahlenwerte (magic number ).
    Absolutes Minimum wäre hier eine Eigenschaft oder Konstante.
    Am sinnvollsten aber eine eigene Klasse Umsatzsteuer, um eine zentrale
    Stelle zu haben, die bei Änderungen betroffen ist -
    denn sollte unserem Finanzminister mal wieder das Geld zu knapp werden
    ändert sich der Steuersatz schneller als man gucken kann ;-)

    Und dann hätte jedes ShopItem eine Eigenschaft Umsatzsteuer
    und eine Überladung für GrossPrice wäre hinfällig.

     

    Ein weiterere (potentielle[1]) Schwachstelle wären die formatierten Werte.
    Da dort keine Culture übergeben wird, wird die aktuelle Kultur des Threads verwendet.
    Was nicht immer das sein wird, was man formattiert sehen möchte,
    mehr siehe Codierung und Lokalisierung

    Für das angehängte "Euro" gilt das oben zu Mehrwertsteuer Gesagte.

     

    Zum Schluß: Die in dem Bereich vorgestellte Klasse DynamicList sollte man
    über eine List(Of T) oder eine Collection(Of T) implementieren.

     

    Das sind nur einige Punkte die mir beim Überfliegen aufgefallen sind.
    Woraus Du jetzt aber auch nicht schließen solltest, dass man das Buch
    gleich wegwerfen sollte ;-) Vielmehr das Eingangs gesagte berücksichtigen,
    und für OOP einen weiteren Titel suchen, der sich damit beschäfttigt.
    Und da es wenige gibt, die direkt für Visual Basic geschrieben sind,
    kann man die zuvor erworbenenen VB Kenntnisse darauf anwenden.

    Gruß Elmar

    [1] Wird es nie als mehr als ein Minishop mag man das ignorieren ;-)

    Freitag, 30. Juli 2010 10:28
  • Hallo Peter,

    besten Dank für Deine Antwort. Jetzt ist mir einiges klarer geworden.

    Zur Antwort auf die zweite Frage hätte ich noch eine Anmerkung. In der Eigenschaft NetPrice findet keine Überprüfung der Werte statt, daher kann man ohne Bedenken in der Basisklasse die Variable myNetPrice als Private deklarieren und in der abgeleiteten Klasse in GrossPrice die Eigenschaft NetPrice verwenden.

    Die Polymorphie betreffend melde ich mich, sollte ich dazu Fragen haben.


    Gruß LittleBlueBird
    Freitag, 30. Juli 2010 11:52
  •  
    Zur Antwort auf die zweite Frage hätte ich noch eine Anmerkung. In der Eigenschaft NetPrice findet keine �?berprüfung der Werte statt, daher kann man ohne Bedenken in der Basisklasse die Variable myNetPrice als Private deklarieren und in der abgeleiteten Klasse in GrossPrice die Eigenschaft NetPrice verwenden.
    Dass keine �?berprüfung stattfindet ist ein derzeitiger Entwicklungsstand. Stell Dir vor, in einiger Zeit wird in der Basisklasse zusätzlicher Code eingebaut, da sich beispielsweise die Anforderungen verändern/verschärfen. Wegen der Kapselung, auf die sich der Entwickler erst einmal verlässt, werden ggf. nicht alle anderen Klassen, z.B. die erbenden Klassen überprüft, ob da etwas nicht mehr passt. Das Ergebnis eines Zugriffs direkt auf die interne (protected) Variablen bewirkt, dass der neu hinzugefügte Code bei Nutzung der erbenden Klasse nicht ausgeführt wird. Ob das so gewollt ist, wird vielleicht erst beim Anwender erkannt. Jedenfalls entspricht die Nutzung geschützter (protected) Variablen in erbenden Klassen anstelle der dazugehörenden Eigenschaften keiner fehlertoleranten Programmierung. In C# ist das Problem noch schärfer, da dort zwischen Gro�?- und Kleinschreibung unterschieden wird und häufig die interne Variable und Eigenschaftsbezeichnung nur durch Gro�?- und Kleinschreibung eines einzelne Buchstabens unterscheiden.
     

    --
    Viele Gruesse
    Peter

    Freitag, 30. Juli 2010 12:06
  • Hallo Elmar,

    herzlichen Dank für die ausführliche Antwort. Anbei meine Kommentare:

    Bei Public Klassen sollte  Protected Friend verwendet werden

    In diesem Fall kann aber aus der Assembly auf die Variablen zugegriffen werden. Dies widerspricht der Kapselung von Klassen. Halte ich persönlich für schlechten Programmierstil.

    Einige weitere Design-Sünden ... Die Konstruktoren mit einem String-Array sind mehr als zweifelhaft ...

    Als Anfänger ist mir das gar nicht aufgefallen. Aber danke für den Hinweis. Bei VB2010 dürfte dies aufgrund der automatischen Eigenschaften keine Entschuldigung mehr sein.

    ... Und dann hätte jedes ShopItem eine Eigenschaft Umsatzsteuer und eine Überladung für GrossPrice wäre hinfällig.

    Ich denke, in diesem Fall wollte der Author die Anwendung der Polymorphie zeigen.

    Ein weiterere (potentielle[1]) Schwachstelle wären die formatierten Werte.

    Das wäre wohl zu viel für den Anfänger. Aber Du hast sicherlich Recht, die Formatierung sollte über Culture dem entsprechendem Land angepasst werden.

    Die in dem Bereich vorgestellte Klasse DynamicList sollte man über eine List(Of T) oder eine Collection(Of T) implementieren.

    Darüber bin ich auch gestolpert. Der Author hätte an dieser Stelle auf diese Möglichkeiten hinweisen müssen, statt eine eigene Klasse zu erstellen. Ich vermute mal, dass dies aus historischen Gründen so ist, schließlich ist das nicht sein erstes VB Buch. Den Code hat er wahrscheinlich aus seinen älteren Ausgaben übernommen. Da gab es vielleicht keine List(Of T) Klassen.

    Zum OOP habe ich folgendes Onlinebuch gefunden:

    http://openbook.galileocomputing.de/oop/

    Ob es was taugt, weiss ich nicht. Werde ich aber mal reinschauen, um ein generelles Verständnis von objektorientierter Programmierung zu bekommen.

    Nochmals danke insbesondere für die Kommentare und die neuen Aspekte.

     


    Gruß LittleBlueBird
    Freitag, 30. Juli 2010 12:51
  • Hallo,

    Die Aussage Bei Public Klassen sollte  Protected Friend verwendet werden ist schlicht falsch.
    Da habe ich wohl falsch kopiert, als ich den Link eingefügt hatte :-( Stehen sollte da:

    Bei Public Klassen, die in einer eigenen Assembly stehen,
    wäre die Verwendung von Friend Protected vorzuziehen.
    (sonst findet sich immer einer, der eine Abkürzung gehen will ;-)

    Denn bei öffentlichen Klassen kann sonst jeder erben - bei friend ist
    das zumindest auf den kleineren Bereich einer Assembly eingeschränkt.
    Die Trennung in unterschiedliche Assemblies für Datenklassen usw. gehört bei mir einfach dazu,
    so dass ich es nicht erwähnt habe -  entschuldige die schlampige Aussage meinerseits.

    Was die Umsatzsteuer (und andere Werte) angeht:
    Manche Dinge tut man einfach nicht - das Minimum wären dort Konstanten.
    Und wenn man Polymorphie zeigen will, so sollte die Grundlage dazu,
    d. h. die Kapselung von Informationen in Klassen (wo die Umsatzsteuer hingehört)
    selbstverständlich dazu gehören. Ohne saubere Kapselung braucht man
    Polymorphie erst gar nicht anzufangen.

    Wie gesagt, dem Autor nur bedingt anzulasten. Mehr das Problem,
    wenn man auf der einen Seite viel (lebensnahes) zeigen möchte,
    auf der anderen Seite die Beispiele knapp halten will/muß.

    Was die DynamicList (und einiges mehr angeht):
    Ich kenne solche Bücher nicht (und die PDFs nur oberflächlich,
    da für mich nicht interessant genug):
    Aber so vieles dürfte aus einer älteren Auflage stammen und
    manches nur leicht (oder gar nicht) angepasst worden sein.
    Und zu .NET 1.x Zeiten schrieb man häufiger solche Klassen,
    wenn man nicht CollectionBase verwenden wollte.

    Was Bücher angeht:
    Das Gallileo Buch stellt eine allgemeine Einführung dar.
    Die Beispiele greifen meist auf Java (älteren Datums) zurück,
    wo vieles sehr ähnlich wie in .NET läuft (aber auch nicht immer).

    Dass ein Buch alles abdeckt, sollte man nicht erwarten -
    das Thema füllt seit Jahrzehnten Bände und ein vollständiger Konsens,
    wie man es richtig macht, ist bisheute nicht gefunden worden.

    Grundlagenbücher, die man gelesen haben sollte, wäre das GOF Buch
    oder eine der Varianten davon - wobei man vieles davon auch im Web findet.

    Zwei weitere Titel, die mehr als nur OOP - was nur Mittel zum Zweck ist - behandeln:
    Code Complete - Deutsche Ausgabe der Second Edition
    Clean Code. Deutsche Ausgabe (dazu siehe auch http://clean-code-developer.de/ )
    Wobei Du dort Visual Basic immer nur am Rande finden wirst -
    aber letztendlich ist die Programmiersprache dabei egal.

     

    Gruß Elmar

    Freitag, 30. Juli 2010 14:42