[OOP-Theorie] Wie bekommen Interfaces eigenen Code oder Mixin-Klasse in VB.NET

    • VB.NET

    Es gibt 7 Antworten in diesem Thema. Der letzte Beitrag () ist von raist10.

      [OOP-Theorie] Wie bekommen Interfaces eigenen Code oder Mixin-Klasse in VB.NET

      Da ich heute zufälligerweise auf die Lösung für ein Problem gestossen an dem ich in VB.NET schon länger rumüberlege und dazu im Internet auch relativ wenig gefunden habe, habe ich gedacht ich schreibe es hier mal nieder ... vielleicht gibt es noch andere die sich mit dem Problem rumschlagen und noch nach einer Lösung suchen.

      Wer sich mal intensiver mit objektorientierter Programmierung auseinander gesetzt hat kennt die große Schwäche von OOP ... Methoden/Funktionen die in mehreren unterschiedlichen Objekten benötigt werden:

      • Vererbung an sich scheidet i.d.R. aus, da .NET keine mehrfach Vererbung zu lässt

      • Public Shared würde sich anbieten, aber ist auch nicht wirklich das Wahre da man doch öfters mal lieber besser gegen Schnittstelle statt gegen Implementierung programmieren möchte und es irgendwann nervend wird wenn man in meheren nach Aufgaben strikt getrennten Klassen/Objekten die Methoden verteilt hat ... ab einer gewissen Projektgröße wird das schnell sehr unübersichtlich und Änderungen führen zu unvorhergesehenen Auswirkungen

      • Interfaces wären eine gute Idee, sind aber vom Prinzip her nur definierende Konstrukte ohne eigene Code-Ausführung

      • Extensions wäre noch ein Ansatz, aber man müsste jedesmal jede benötigte Extension auf das Objekt programmieren ... werden Funktionen bei größeren Projekten für viele Objekte benötigt kann sich jeder vorstellen wie unübersichtlich das Ganze wird

      • eigene Objekte die man dann bei Bedarf als Member der Klasse lädt, dabei lässt sich dann aber nicht vermeiden das passiert was eigentlich im OOP nicht passieren sollte: Objekte führen mehrfache ähnliche/gleichartige Funktionen mit sich rum die an sich nichts mit der Aufgabe des Objektes an sich zu tun haben ... Pflege, Wartung, Änderung, Übersichtlichkeit sind alles Dinge die dabei leiden ... und wer mag schon wirklich jede Menge Objekte als Klassen-Member mit sich rumführen nur damit er die ein oder andere Methode nutzen kann ... von der extremen Code-Redunanz (Objekt deklarieren, initialisieren, Methode ausführen, möglicherweise danach aufräumen) mal ganz abgesehen ... Using ist da sicherlich ein besserer Weg, aber letztendlich programmiert man dann wieder gegen Implementierung und nicht gegen Schnittstelle

      • Module ... eh klar, funktioniert natürlich ... aber das das nicht mehr wirklich was mit OOP zu tun hat brauchen wir ja nicht zu diskutieren und die daraus möglicherweise resultierenden Probleme bei größeren Projekten sind hinlänglich bekannt


      In dem Sinne gibt es sicherlich viele Ansätze das Problem in .NET zu lösen, aber keine der Lösungen ist wirklich in allen Belangen befriedigend.

      Da mich das Thema länger beschäftigt suche ich auch schon seit einiger Zeit nach einer Lösung die "perfekt" wäre. Dabei bin ich auf das Thema Mixin-Klasse wie sie in Ruby angeboten werden gestossen ... optimal eigentlich das man dort solche mehrfach benötigten Methoden und Eigenschaften thematisch sortiert reinpackt und die Mixin-Klassen die man dann braucht in das eigentlich Objekt implementiert (nicht vererbt!) ... an sich perfekt, denn eine Implementations-Zeile reicht und dem Objekt stehen alle Methoden und Eigenschaften der Mixin-Klasse zur Verfügung. Dabei erfüllt die Mixin-Klasse das Prinzip der Programmierung gegen Schnittstelle ... Vorteile dieser Art der Programmierung sind ja hinlänglich bekannt.

      Aber ... Mixin-Klassen gibt es in .NET eben nicht.

      Zufälligerweise habe ich heute mich mal wieder mit dem Thema beschäftigt und einfach mal los gegoogelt ob es in den Weiten des Internets was gibt was ich bis jetzt übersehen hatte und tatsächlich bin ich auf einen englischen extrem kurzen Blog gestossen der den entscheidenden Tipp enthielt:

      Man kombiniere Interfaces mit Extension und schon hat man Mixin-Klassen in VB.NET und Interfaces bekommen Methoden mit ausführbaren Code.


      Habe natürlich erstmal dumm geguckt ... Objekte/klassen mit Extensions zu erweitern .. ja klar, bekannt ... aber Interfaces mit Extension zu versehen war mir neu, bzw. hatte ich noch nie daran gedacht.

      Habe dann mal ein wenig damit rumgespielt und mir dämmert dann das es für viele meiner "Probleme" die perfekte Lösung ist:

      • Man kann beliebig viele Infertfaces in eine Klasse implementieren, daher ist die thematisch Anordnung von Methoden und Eigenschaften kein Problem

      • Extension zu einem Interfaces müssen nicht einzeln in den Klassen implementiert werden im Gegensatz zu den Interface Methoden

      • Extension schreibt man einmal zu einem Interface und nicht jedes Mal einzeln für jedes Objekt das die Extension bekommen soll

      • Extension zu einem Interface können ganz normal Code beinhalten, bzw. sich bei anderen Objekten ganz normal bedienen ... Thema Programmierung gegen Schnittstelle

      • Redunanter Code geht gen Null ... die klassische Implementationszeile reicht aus und schon stehen der Klasse alle Extensions des Interface als nutzbare Methoden/Eigenschaften zur Verfügung

      • Eine Klasse wird extrem viel übersichtlicher da alle sonst benötigten Objekt-Deklarationen/-Initialisierungen komplett wegfallen, die Extension eines Interfaces kann man ganz normal ansprechen wie eine Klassen-Methode ... z.B. Me.MeineInterfaceExtension und gut ist ... es wird alles wesentlich schlanker und übersichtlicher

      • Man braucht noch nichtmal eine Interface-Methode im Interface anzugeben und trotzdem kann das Objekt das das Interface implementiert die Extensions nutzen

      • Projektspezifische Methoden können extrem einfach ausgetauscht werden, da die Objekte nun nicht mehr die Implementierung der projektspezifischen Klassen nutzen sondern über das Interface mit seinen Extensions


      Als Beispiel mal eine Möglichkeit des Praxiseinsatzes:

      Z.B. Prüfungen von Zugriffs-/Bearbeitungsrechte auf Datenbanken lassen sich so optimal in ein Projekt implementieren. Objekte die dann Datenoperationen durchführen implementieren das Interface. Die einzelnen Prüfungs-Methoden sind dem Interface als Extension zugeordnet, wobei die Extensions nix anderes tun als sich bei dem projektspezifischen Objekt für Zugriffs-/Bearbeitungsrechte zu bedienen.

      Will man nun die Objekte die die Datenoperationen ausführen in ein anderes Projekt implementieren, kopiert man einfach nur das Modul mit der Interface-Deklaration und den dazu passenden Extension mit und programmiert die für das Projekt gültige Zugriffs-/Bearbeitungsrechte-Prüfung gegen die Extensions des Interfaces. Da die Extensions bereits vorgeben welche Daten in welcher Form von den Datenbearbeitungs-Objekte kommen und welche Rückmeldung erwartet wird, ist die objektspezifische Programmierung dann mehr als simpel und vor allem sicher vom Handling her das sie ja in diesem Falle fast wie ein Pattern funktionieren (das kann man natürlich zusätzlich nutzen).

      Ich hoffe ich konnte dem ein oder anderen mit diesem Tipp helfen. Möglich das das schon vielen bekannt ist, aber der könnte ja noch das ein oder andere was ich vergessen habe sollte ergänzen. Sollten das schon allen bekannt sein, dann bitte löschen ... wobei mich dann interessieren täte wieso man von diesem kleinen Kniff so gut wie nichts im Internet dazu findet. ^^

      Falls jemand Bedarf an Beispiel-Code dazu hat, kann ich den gerne nachliefern. Einfach kurz Bescheid geben.

      Gruß

      Rainer

      Nibel schrieb:

      Anscheinend gibt es hier eine entsprechende Lib, die das bereitstellt. Ausprobiert habe ich nichts...


      Danke für die Info. Aber nachdem ich nun die Lösung wie im Opener-Post beschrieben für meine Problemchen gefunden habe ist mir das auf dem Weg angenehmer als eine fremde Lib zu nutzen. Aber klar ... wer es lieber mit Lib mag kann das auch so nutzen.

      @ Topic

      Auf Grund einer kleineren Diskussion zu dem Thema habe ich mal einen Beispielcode erzeugt der nur kurz und simple das Prinzip erklärt. Daher erstmal eine Danke an ErfinderDesRades der mich dazu "getrieben" hat den Beispielcode zu erstellen. ^^

      VB.NET-Quellcode

      1. Imports System.Runtime.CompilerServices
      2. '// Abstract masterclass
      3. Public MustInherit Class Fahrzeug
      4. Public MustOverride Function MovingDirection() As String
      5. End Class
      6. '// Class inherits Masterclass and using Interface
      7. Class Auto
      8. Inherits Fahrzeug
      9. Implements IBewegung
      10. Public Overrides Function MovingDirection() As String
      11. Return Me.Direction()
      12. End Function
      13. End Class
      14. '// Class inherits Masterclass and using Interface
      15. Class Flugzeug
      16. Inherits Fahrzeug
      17. Implements IBewegung
      18. Public Overrides Function MovingDirection() As String
      19. Return Me.Direction()
      20. End Function
      21. End Class
      22. '// Class against Interface
      23. Class Bewegung
      24. Public Function MovingDirection() As String
      25. Return "Ich bewege mich"
      26. End Function
      27. End Class
      28. Module mdl_Extension
      29. Interface IBewegung
      30. End Interface
      31. <Extension()> _
      32. Public Function Direction(ByVal interF As IBewegung) As String
      33. Dim Moving As New Bewegung()
      34. Try
      35. Return Moving.MovingDirection
      36. Finally
      37. '// clean up section if needed
      38. Moving = Nothing
      39. End Try
      40. End Function
      41. End Module
      42. Module Module1
      43. Sub Main()
      44. Dim clsAuto As New Auto
      45. MsgBox(clsAuto.MovingDirection)
      46. Dim clsFlugzeug As New Flugzeug
      47. MsgBox(clsFlugzeug.MovingDirection)
      48. End Sub
      49. End Module


      In dem Beispiel ist das auslösende Problem das alle Fahrzeug-Objekte von der abstrakten Masterclass Fahrzeuge erben.

      Aber egal welches Fahrzeug man nimmt allesamt haben einige Eigenschaften und Methoden gemeinsam, wie z.B. Bewegungs-Richtung, Geschwindigkeit, Ausstattungsdetails und ähnliche gemeinsame Dinge. Also alles Sachen für die es sinnvoll ist eigene Objekte zu erstellen die dann das Handling der Berechnung/Rückgabe für die Child-Klassen (in dem Falle Auto und Flugzeug) übernehmen.

      Durch die Lösung des Interfaces mit den Extensions die direkt zum Interface zugeordnet sind ist es nun ein einfaches für alle die o.g. Dinge eigene Klassen zu erstellen (im obigen Beispiel eben die Klasse Bewegung) und diese gegen das Interface mit den dazugehörigen Extensions zu proggen (IBewegung mit der Extension Direction).

      Die Child-Classes der Masterclass Fahrzeug implementieren dann die benötigten Schnittstellen (z.B. braucht ein Fahrrad keine Methoden und Eigenschaften für einen Motor, aber ein Auto oder ein Motorrad brauchen diese dann logischerweise).

      Natürlich könnte man im Beispiel oben das Interface direkt in die Masterclass implementieren und dann würden deren Extension-Methoden/eigenschaften genauso allen Child-Classes zur Verfügung stehen. Die Implementierung der Schnittstelle direkt in Child-Classes soll hier eigentlich nur demonstrieren das man je Child-Class nur die Schnittstellen implementiert die diese auch wirklich benötigt (siehe Absatz zuvor von wegen Fahrrad und Schnittstellen Motor).

      Denke obwohl das Beispiel sehr sehr simple gehalten ist und nicht mal im Ansatz alle Nutzungsmöglichkeiten darstellt, macht es doch die Vorteile bei näherer Betrachtung deutlich. Wer Fragen dazu hat, einfach posten. ;)

      Gruß

      Rainer
      Vlt hab ich was übersehen, aber ist nicht ein grundlegendes Problem, Instanzspezifische Daten zu speichern?
      blog.colinmackay.net/archive/2008/02/24/1895.aspx


      In Ergänzung meines vorigen Posts (welcher noch nicht freigeschaltet ist).

      Als Vorbemerkung: 4.0 rox. Denn ohne wäre der folgende Code gar nicht möglich

      Quelle (C#): c-sharpcorner.com/UploadFile/b942f9/5950/

      Bsp:
      Spoiler anzeigen

      VB.NET-Quellcode

      1. ' Diese Klasse zeigt die Möglichkeit, wie man Mixin Klassen (bekannt aus Ruby), welche
      2. ' multiple Vererbung bedeuten, mit .Net simulieren kann.
      3. ' Hintergründe etc siehe:
      4. ' http://www.c-sharpcorner.com/UploadFile/b942f9/5950/
      5. Imports System.Runtime.CompilerServices
      6. ' Das Interface, dass wir später implementieren enthält KEINE
      7. ' Definitionen!
      8. Public Interface MTreibstofftank
      9. End Interface
      10. ' Unsere Erweiterungsklasse
      11. Public Module Treibstofftank
      12. ' Class used to hold instance data
      13. Private NotInheritable Class Fields
      14. Friend Tankgroesse As Integer = 0
      15. Friend Tankinhalt As Integer = 0
      16. Friend VerbrauchPro100 As Double = 1
      17. End Class
      18. ' Der conditionalweaktable ist der "Trick", der erst ab Net 4.0 möglich ist
      19. ' siehe: http://msdn.microsoft.com/de-de/library/dd287757.aspx
      20. ' Denn das erlaubt dem Garbage Collector das ursprümgliche Object UND
      21. ' diese Referenz aufzuräumen!
      22. Private WeakTable As New ConditionalWeakTable(Of MTreibstofftank, Fields)
      23. #Region "Tankgroesse"
      24. <Extension()> Public Function GetTankgroesse(ByVal mtt As MTreibstofftank) As Integer
      25. Return WeakTable.GetOrCreateValue(mtt).Tankgroesse
      26. End Function
      27. <Extension()> Friend Sub SetTankgroesse(ByVal mtt As MTreibstofftank, ByVal value As Integer)
      28. WeakTable.GetOrCreateValue(mtt).Tankgroesse = value
      29. If WeakTable.GetOrCreateValue(mtt).Tankinhalt > value Then WeakTable.GetOrCreateValue(mtt).Tankinhalt = value
      30. End Sub
      31. #End Region
      32. #Region "Tankinhalt"
      33. <Extension()> Public Function GetTankinhalt(ByVal mtt As MTreibstofftank) As Integer
      34. Return WeakTable.GetOrCreateValue(mtt).Tankinhalt
      35. End Function
      36. <Extension()> Public Sub SetTankinhalt(ByVal mtt As MTreibstofftank, ByVal value As Integer)
      37. With WeakTable.GetOrCreateValue(mtt)
      38. If value > .Tankgroesse Then value = .Tankgroesse
      39. .Tankinhalt = value
      40. End With
      41. End Sub
      42. #End Region
      43. #Region "Verbrauch"
      44. <Extension()> Public Function GetVerbrauch(ByVal mtt As MTreibstofftank) As Double
      45. Return WeakTable.GetOrCreateValue(mtt).VerbrauchPro100
      46. End Function
      47. <Extension()> Public Sub SetVerbrauch(ByVal mtt As MTreibstofftank, ByVal value As Double)
      48. If value <= 0 Then Throw New System.ArgumentException("Value must be greater than 0", value)
      49. WeakTable.GetOrCreateValue(mtt).VerbrauchPro100 = value
      50. End Sub
      51. <Extension()> Public Function Reichweite(ByVal mtt As MTreibstofftank) As Decimal
      52. With WeakTable.GetOrCreateValue(mtt)
      53. Return Math.Round(.Tankinhalt / .VerbrauchPro100 * 100.0, 0)
      54. End With
      55. End Function
      56. #End Region
      57. End Module
      58. ' Eine Basisklasse, von der wir ableiten
      59. Public MustInherit Class Fahrzeug
      60. Public Property Name As String
      61. End Class
      62. ' Unsere Beispiel-Klasse
      63. ' Kein Code, aber jede Menge Funktion ;)
      64. Public Class Auto
      65. Inherits Fahrzeug
      66. Implements MTreibstofftank
      67. End Class


      Man beachte die Klasse "Auto"! Nix drin, aber funzt trotzdem. UND dank ConditionalWeakTable kann man "darin" sogar Werte speichern/lesen.

      Anwendung zb:

      VB.NET-Quellcode

      1. Dim a As New Auto With {.Name = "BMW Z3"}
      2. a.SetTankgroesse(50)
      3. a.SetTankinhalt(50)
      4. a.SetVerbrauch(10)
      5. Debug.Print(a.Name & ": " & a.Reichweite)
      6. Dim b As New Auto With {.Name = "Lupo 3L"}
      7. b.SetTankgroesse(32)
      8. b.SetTankinhalt(32)
      9. b.SetVerbrauch(2.9)
      10. Debug.Print(b.Name & ": " & b.Reichweite)
      11. ' unser BMW "lebt" weiter ...
      12. Debug.Print(a.Name & ": " & a.Reichweite)


      Edit by ~blaze~:
      *2 Beiträge zusammengefügt*

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „~blaze~“ ()

      Interessanter Ansatz.
      Grundsätzlich sollte man eh Schnittstellen der Vererbung vorziehen. Habe dazu einiges in Büchern über Entwurfsmuster gelesen.

      Habe dazu aber noch eine Frage.
      Du schreibst ja, dass man die Methoden nur da implementieren muss, wo man sie braucht. Also z.B. bräuchte die Klasse Fahrrad keine Implementierung einer Klasse Motor. So weit, so gut. Was passiert, wenn ich aber ein Objekt in eine Schnittstelle caste?

      Beispiel:

      VB.NET-Quellcode

      1. Dim myFahrrad as Object = new Fahrrad() 'Fahrrad implementiert deine Schnittstelle, und die Schnittstelle hat eine Erweiterung zum Erzeugen vom Motor
      2. Dim myBewegung as IBewegung = DirctCast(myFahrrad, IBewegung)
      3. myBewegung.PS …


      In dem Falle hat myBewegung ja auf alles Zugriff, was die Schnittstelle bietet, da ja bei Schnittstellen davon ausgegangen wird, dass man die Applikation ohne diese Implementierung nicht kompilieren kann.

      Was passiert nun, wenn ich beim Objekt Fahrrad über die Schnittstellendefinition auf die PS-Zahl des Motors zugreife? Die Schnittstelle setzt voraus, dass es implementiert ist, aber Fahrrad hätte es nicht implementiert. Oder verstehe ich das falsch?


      Das von mir beschriebene Vorgehen ist natürlich simpel gehalten. Oft kommt es eher so vor, dass eine Methode einer Klasse eine Schnittstelle als Parameter (z.B. „Sub MySub(ByVal MyParam1 as IBewegung)“) erwartet, um verschiedene Klassentypen aufnehmen zu können. Da würde es schnell passieren, dass man nicht implementierte Methoden oder Eigenschaften aufruft.
      @ picoflop

      Juup ... auf das Thema ConditionalWeakTable bin ich auch schon gestossen und hatte mir auch gedacht .... 4.0 einfach nur goil. ^^

      Hatte mich aber noch nicht eingearbeitet weil der Bedarf noch nicht da war. Der Ursprungsgedanke war ja letztendlich erstmal nur "einfache" Methoden/Eigenschaften die ich an vielen verschiedenen Stellen brauche mit wenig Aufwand und vor allem mit möglichst wenig redunantem Code zur Verfügung zu stellen.

      Aber mit den ConditionalWeakTables wird das Konzept natürlich massiv aufgebohrt und bekommt weitere ungeahnte Möglichkeiten dazu.

      Auf jeden Fall Danke für Deinen Code ... das ist definitiv eine hochspannende Geschichte.

      @ GambaJo

      Du hast mich da ein wenig falsch verstanden ... okay, vermutlich habe ich das Beispiel auch nicht richtig unmißverständlich aufgebaut.

      Die Methoden/Eigenschaften von Motor würde in meinem Beispiel NICHT in das Interface IBewegung eingefügt sein, sondern natürlich im Interface IMotor.

      Somit würde die Klasse Fahrrad dann nur das Interface IBewegung beinhalten, aber die Klasse Auto würde IBewegung UND IMotor beinhalten. Und damit ist die Welt wieder rund und sicher. ^^

      Aber Du hast Recht ... man muss bei dem Konzept natürlich aufpassen das man kein Interface implementiert das Funktionen/Methoden bereit stellt die nicht für die Klasse geeignet sind.

      Wobei ich bei Interfaces die zwingend gewisse Eigenschaften des Objektes erwarten/vorraussetzen damit sie funktionieren (eben Interface IMotor setzt zwingend das Vorhandensein eines Motors im implementierten Objekt vorraus um funktionieren zu können) dann Interface-Deklarationen nutzen würden.

      Ich habe mal versucht das ganze in dem Beispielcode aus meinem zweiten Post umzusetzen damit es vllt verständlicher wird:

      Spoiler anzeigen

      VB.NET-Quellcode

      1. Imports System.Runtime.CompilerServices
      2. '// Abstract masterclass
      3. Public MustInherit Class Fahrzeug
      4. Public MustOverride Function MovingDirection() As String
      5. Public MustOverride ReadOnly Property MovingType() As String
      6. End Class
      7. '// Class inherits Masterclass and using Interface
      8. Class Auto
      9. Inherits Fahrzeug
      10. Implements IBewegung
      11. Implements IMotor
      12. Public Overrides ReadOnly Property MovingType() As String
      13. Get
      14. Return Me.MotorInformation(Me)
      15. End Get
      16. End Property
      17. Public Overrides Function MovingDirection() As String
      18. Return Me.Direction()
      19. End Function
      20. Public ReadOnly Property MotorArt As mdl_Extension.IMotor.EnMotorArt Implements mdl_Extension.IMotor.MotorArt
      21. Get
      22. Return IMotor.EnMotorArt.OttoMotor
      23. End Get
      24. End Property
      25. Public ReadOnly Property MotorLeistung As Integer Implements mdl_Extension.IMotor.MotorLeistung
      26. Get
      27. Return 198
      28. End Get
      29. End Property
      30. End Class
      31. '// Class inherits Masterclass and using Interface
      32. Class Flugzeug
      33. Inherits Fahrzeug
      34. Implements IBewegung
      35. Implements IMotor
      36. Public Overrides ReadOnly Property MovingType() As String
      37. Get
      38. Return Me.MotorInformation(Me)
      39. End Get
      40. End Property
      41. Public Overrides Function MovingDirection() As String
      42. Return Me.Direction()
      43. End Function
      44. Public ReadOnly Property MotorArt As mdl_Extension.IMotor.EnMotorArt Implements mdl_Extension.IMotor.MotorArt
      45. Get
      46. Return IMotor.EnMotorArt.Düsentriebwerk
      47. End Get
      48. End Property
      49. Public ReadOnly Property MotorLeistung As Integer Implements mdl_Extension.IMotor.MotorLeistung
      50. Get
      51. Return 1900
      52. End Get
      53. End Property
      54. End Class
      55. '// Class inherits Masterclass and using Interface
      56. Class Fahrrad
      57. Inherits Fahrzeug
      58. Implements IBewegung
      59. Public Overrides ReadOnly Property MovingType() As String
      60. Get
      61. Return "Antrieb nur über Muskelkraft"
      62. End Get
      63. End Property
      64. Public Overrides Function MovingDirection() As String
      65. Return Me.Direction()
      66. End Function
      67. End Class
      68. '// Class against Interface
      69. Class Bewegung
      70. Public Function MovingDirection() As String
      71. Return "Ich bewege mich"
      72. End Function
      73. End Class
      74. Module mdl_Extension
      75. Interface IMotor
      76. Enum EnMotorArt
      77. OttoMotor = 1
      78. HybridMotor = 2
      79. Düsentriebwerk = 3
      80. End Enum
      81. ReadOnly Property MotorLeistung() As Integer
      82. ReadOnly Property MotorArt() As EnMotorArt
      83. End Interface
      84. <Extension()> _
      85. Public Function MotorInformation(ByVal interF As IMotor, ByVal Fahrzeug As IMotor) As String
      86. Return "Antriebs-Art " & [Enum].GetName(GetType(IMotor.EnMotorArt), Fahrzeug.MotorArt) & " der " & Fahrzeug.MotorLeistung & " PS leistet"
      87. End Function
      88. Interface IBewegung
      89. End Interface
      90. <Extension()> _
      91. Public Function Direction(ByVal interF As IBewegung) As String
      92. Dim Moving As New Bewegung()
      93. Try
      94. Return Moving.MovingDirection
      95. Finally
      96. '// clean up section if needed
      97. Moving = Nothing
      98. End Try
      99. End Function
      100. End Module
      101. Module Module1
      102. Sub Main()
      103. Dim clsAuto As New Auto
      104. MsgBox(clsAuto.MovingDirection & " mit " & clsAuto.MovingType)
      105. Dim clsFlugzeug As New Flugzeug
      106. MsgBox(clsFlugzeug.MovingDirection & " mit " & clsFlugzeug.MovingType)
      107. Dim clsFahrrad As New Fahrrad
      108. MsgBox(clsFahrrad.MovingDirection & " mit " & clsFahrrad.MovingType)
      109. '// diese Anweisung funktioniert weil Flugzeug die Schnittstelle IMotor
      110. '// implementiert hat
      111. Dim Motor1 As IMotor = DirectCast(clsFlugzeug, IMotor)
      112. '// diese Anweisung löst einen Fehler aus weil Fahrrad
      113. '// die Schnittstelle IMotor nicht implementiert hat
      114. Dim Motor2 As IMotor = DirectCast(clsFahrrad, IMotor)
      115. End Sub
      116. End Module


      Und im letzten Teil der Sub Main() siehst Du das das was Du befürchtest nicht möglich ist, wenn man nur die Schnittstellen auch implementiert die für die Klasse zur Verfügung stehen.

      Aber trenne Dich trotzdem mal von dem Grundgedanken der Interfaces einer der Hauptideen der Geschichte ist ja auch Methoden/eigenschaften zur Verfügung zu stellen die verschiedene Objekte nutzen, die aber nicht vordergründing im Zusammenhang mit der Aufgabe das Objektes stehen.

      Wie die Prüfung von Zugriffsrechten auf eine Datenbank, eben ob der User der sich angemeldet hat auch die entsprechende Berechtigung besitzt:

      Du hat in einer App mehrere Forms die für die Anzeige und Bearbeitung von Datensätzen zuständig sind. Nun musst Du an verschiedenen Stellen in Deiner App prüfen ob der User das überhaupt darf. Also bei dem Aufruf einer Form für Datensätze prüfen ob der User die nur lesend oder lesend und schreibend bearbeiten darf.

      Hier ist es natürlich sinnvoll ein eigenes Objekt zu erstellen das die Prüfung vornimmt und ein Feedback zurück wirft was der User nun darf und was nicht.

      Im Normalfall müsstest Du jetzt überall im Code wo Du die Prüfung brauchst das Objekt initialisieren und das Feedback abrufen. Durch die obige Methode erledigt sich das. Du implementierst überall nur die Schnittstelle IDbCheckAccess und dazu gibt es die Extension UserAccess. In der Extension wird das Prüfungs-Objekt initialisiert und das Feedback abgeholt und zurück geworfen.

      Also anstatt überall das stehen zu haben:

      VB.NET-Quellcode

      1. Dim CheckObj As New xxxx.MeinDBZugriffsCheckObjet
      2. if CheckObj.UserAccess = schreiben then
      3. ...


      Hast du überall nur noch eine Zeile stehen:

      VB.NET-Quellcode

      1. if Me.UserAccess = schreiben then


      Und dazu eben der Vorteil der Programmierung gegen "Schnittstelle" anstatt gegen "Implementierung".

      Gruß

      Rainer
      @ picoflop

      So, habe mir jetzt mal Zeit genommen und mir das Thema ConditionalWeakTables mal näher angesehen ... und beim zweiten Blick wird es noch genialer als beim ersten.

      Das löst tatsächlich noch das letzte Thema: Objektfremde Datenhaltung und trotzdem für jede Objektinstanz separat.

      Damit kommen interessante Möglichkeiten zu Standen, z.B. Zustände eines Objektes zum Zeitpunkt eines Aufrufes abzuspeichern und diesen Wert dann bei einem weiteren Aufruf zum aktuellen Objektzustand zu vergleichen um Codeverzweigungen in Abhängigkeit der Zustandsveränderung zu realisieren.

      Also um mal in meiner obigen Beispiel-Terminologie zu bleiben, könnte man nun die Zustände für Motor an/aus in dem WeakTable abspeichern und Logik-Fehler durch Abgleich der gespeicherten Zustände vermeiden, wie z.B. ein Motor der den Zustand aus hat kann keine Beschleunigung erzeugen.

      Sowas müsste man ohne diese Weaktables ansonsten entweder in der Klasse Auto oder in der Klasse Motor prüfen. Wäre beides ungeschickt, da diese Prüfung ja gar nicht Aufgabe der Klasse Auto sein darf/kann und in der Klasse Motor die über das Interface IMotor angesprochen wird ist es quasi unmöglich einen Zustand zu einem bestimmten Objekt zu speichern und die soll das eigentlich auch nicht prüfen müssen. Durch die Nutzung der WeakTables kann man nun aber die Motorzustände in der Schnittstelle IMotor abspeichern und bei Ansprache der Extension Beschleunigung dann entsprechend prüfen ob die Methode überhaupt ausgeführt werden darf.

      Wirklich geniale Geschichte diese WeakTables und nochmal danke an Dich für den Hinweis, ohne den hätte ich vermutlich das Thema WeakTables irgendwann wieder komplett vergessen, bzw. mich gar nicht erst näher mit Ihnen beschäftigt.

      Gruß

      Rainer