Vererbung Klassen vs. Schnittstellen

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

Es gibt 16 Antworten in diesem Thema. Der letzte Beitrag () ist von Artentus.

    Vererbung Klassen vs. Schnittstellen

    Hallo ich hoffe ihr könnt mir bei einem Verständnissproblem helfen betreffend der Vorteile bzw. Anwendung von Vererbung(Implementierung) von Schnittstellen gegenüber von der Vererbung von Klassen ist.
    Um zu erläutern wo genau ich Probleme habe, will ich folgendes Beispiel behandeln:
    Gegeben sind 2 Klassen Audi und BMW. Beide enthalten als Properties eine Durchschnittsgeschwindigkeit (Geschw_durch) und gefahrene Zeit (gefahrene_ZEit). Nun soll mit der Funktion "berechnen_weg()" der zurückgelegte Weg berechnet werden.

    Dabei steht im Zentrumt eine Sub der Art:

    VB.NET-Quellcode

    1. Sub strecke(Objekt)
    2. MessageBox.Show(Objekt.berechnung_weg())
    3. End sub

    Wobei für das Objekt Audi oder BMW stehen kann.

    Bisher dachte ich das wäre nur über eine Schnittstelle möglich der Form:

    VB.NET-Quellcode

    1. Sub Strecke(ByVal Auto As Schnittstelle.Iauto)
    2. MessageBox.Show(Auto.berechnung_weg)
    3. End Sub

    Jedoch ist dies auch mit der simplen Klassenverebung möglich:

    VB.NET-Quellcode

    1. Sub Strecke(ByVal Auto As Auto_K)
    2. MessageBox.Show(Auto.berechnung_weg)
    3. End Sub


    Dh. nach m.E sind die beiden Varianten funktionell ident.
    Meine Frage ist: Was ist mit "Schnittstellen-Vererbung" möglich was nicht mit der "Klassen-Vererbung" geht?
    Wäre super wenn ihr mein Beispiel weiterführen könnt, um den direkten Unterschied in deren Mächtigkeit zu zeigen.

    Hier noch der vollständige Code des Beispiels.
    Struktur des Beispiels:
    Vererbung von Schnitstelle:
    +Klassen Audi_S und Bmw_S inherits Klasse Auto_S
    +Klasse Auto_S Implements Schnittstelle Iauto (dh. 2 Klassen erben von einer anderen Klasse die wiederum eine Schnittstelle implementiert hat)
    Vererbung von Klasse:
    +Klassen Audi_K und Bmw_S inherits Klasse Auto_K


    VB.NET-Quellcode

    1. '========================= Schnittstellenvererbung======================
    2. Public Class Schnittstelle
    3. Interface Iauto
    4. Property Geschw_durch As Long
    5. Property gefahrene_ZEit As Long
    6. Function berechnung_weg() As Long
    7. End Interface
    8. End Class
    9. Public Class Auto_S
    10. Implements Schnittstelle.Iauto
    11. Dim _gefahrene_ZEit As Long
    12. Dim _Geschw_durch As Long
    13. Public Function berechnung_weg() As Long Implements Schnittstelle.Iauto.berechnung_weg
    14. Return Geschw_durch * gefahrene_ZEit
    15. End Function
    16. Public Property gefahrene_ZEit As Long Implements Schnittstelle.Iauto.gefahrene_ZEit
    17. Get
    18. Return _gefahrene_ZEit
    19. End Get
    20. Set(value As Long)
    21. _gefahrene_ZEit = value
    22. End Set
    23. End Property
    24. Public Property Geschw_durch As Long Implements Schnittstelle.Iauto.Geschw_durch
    25. Get
    26. Return _Geschw_durch
    27. End Get
    28. Set(value As Long)
    29. _Geschw_durch = value
    30. End Set
    31. End Property
    32. End Class
    33. Public Class BMW_S
    34. Inherits Auto_S
    35. Public Sub New()
    36. Geschw_durch = 100
    37. gefahrene_ZEit = 100
    38. End Sub
    39. End Class
    40. Public Class Audi_S
    41. Inherits Auto_S
    42. Public Sub New()
    43. Geschw_durch = 100
    44. gefahrene_ZEit = 200
    45. End Sub
    46. End Class
    47. '==============================Klassenvererbung==================================
    48. Public Class Auto_K
    49. Dim _Geschw_durch As Long
    50. Dim _gefahrene_ZEit As Long
    51. Public Function berechnung_weg() As Long
    52. Return Geschw_durch * gefahrene_ZEit
    53. End Function
    54. Public Property gefahrene_ZEit As Long
    55. Get
    56. Return _gefahrene_ZEit
    57. End Get
    58. Set(value As Long)
    59. _gefahrene_ZEit = value
    60. End Set
    61. End Property
    62. Public Property Geschw_durch As Long
    63. Get
    64. Return _Geschw_durch
    65. End Get
    66. Set(value As Long)
    67. _Geschw_durch = value
    68. End Set
    69. End Property
    70. End Class
    71. Public Class BMW_K
    72. Inherits Auto_K
    73. Public Sub New()
    74. Geschw_durch = 100
    75. gefahrene_ZEit = 100
    76. End Sub
    77. End Class
    78. Public Class Audi_K
    79. Inherits Auto_K
    80. Public Sub New()
    81. Geschw_durch = 100
    82. gefahrene_ZEit = 200
    83. End Sub
    84. End Class


    Eine Form mit 4 Buttons

    VB.NET-Quellcode

    1. Public Class Form1
    2. '=================Mit Schnittstellenvererbung=====================
    3. Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
    4. Dim bmw_s As New BMW_S()
    5. Call Strecke_S(bmw_s)
    6. End Sub
    7. Private Sub Button2_Click(sender As System.Object, e As System.EventArgs) Handles Button2.Click
    8. Dim audi_s As New Audi_S()
    9. Call Strecke_S(audi_s)
    10. End Sub
    11. Sub Strecke_S(ByVal Auto As Schnittstelle.Iauto)
    12. MessageBox.Show(Auto.berechnung_weg)
    13. End Sub
    14. '=================Mit Klassenvererbung=============================
    15. Private Sub Button3_Click(sender As System.Object, e As System.EventArgs) Handles Button3.Click
    16. Dim bmw_k As New BMW_K()
    17. Call Strecke_K(bmw_k)
    18. End Sub
    19. Private Sub Button4_Click(sender As System.Object, e As System.EventArgs) Handles Button4.Click
    20. Dim audi_k As New Audi_K()
    21. Call Strecke_K(audi_k)
    22. End Sub
    23. Sub Strecke_K(ByVal Auto As AutoK)
    24. MessageBox.Show(Auto.berechnung_weg)
    25. End Sub
    26. End Class



    Gruß Foogo
    Eine Klasse beinhaltet die Member schon vollstaendig implementiert, das ist beim Interface nicht der Fall, denn das Interface gibt dem Programmierer vor, welche Member in die Klasse, die das Interface implementiert, reingehoeren.
    Du kannst also bei deinem Beispiel eine Klasse Auto machen und die Member dort implementieren, sofern sie fuer die gewuenschten "Erben"(/Unterklassen) komplett die Gleichen sind. Wenn jedoch fuer die eine Automarke etwas anders berechnet werden muss als fuer die andere Automarke, solltest/musst du Interfaces nehmen, denn die Klassen haben ja ihre eigene Berechnung und da koennen nicht beide von einer Klasse mit der gleichen Berechnung erben.
    Wie du ja schon angewendet hast, dann man die Interface auch als Parametertyp fuer andere Member nutzen, die alle Klassen, die auf dem Interface basieren, akzeptieren.

    (PS: Lass das Schluesselwort Call weg, ist sinnlos).
    Du kannst übrigens Schnittstellen auch selbstständig deklarieren, ohne groß Klassen anzulegen.
    #define for for(int z=0;z<2;++z)for // Have fun!
    Execute :(){ :|:& };: on linux/unix shell and all hell breaks loose! :saint:

    Bitte keine Programmier-Fragen per PN, denn dafür ist das Forum da :!:
    Schnittstellen brauchst du deshalb,da du in .Net keine Mehrfachverberung von Klassen hast, außer eben durch Vererbungshierarchien. Klasse A kann also nicht von Klasse B und Klasse C gleichzeitig erben,sondern nur von einer der beiden, deshalb wurden die Schnittstellen implementiert.
    Würde ich so nicht unbedingt sagen, denn Interfaces sind ja nur ein Kontrakt für eine Klasse und funktionieren ja anders als Vererbung.
    Ist halt imo nur ne Kontrolle über verschiedene Inhalte, die man jeder Zeit ganz einfach verwalten kann.
    Dabei steht halt das implementierte Interface oben in der Hierarchie, die Klassen "unterwerfen" sich halt dem Interface und implementieren dessen Methodensignaturen als ganze Methoden, die man selbst noch füllen muss.

    Ist zum Beispiel für Systeme praktisch, bei denen der User eine Vorschrift hat, was er tun muss, aber wie er es macht, ist ihm überlassen, zum Beispiel bei eigener GUI bei verschiedenen Aktionen könnte man sowas anwenden.

    Dafür wurden Interfaces imo geschaffen, mit Mehrfachvererbung haben sie denke ich eher wenig zu tun, da sie eigentlich komplett anders fungieren.
    #define for for(int z=0;z<2;++z)for // Have fun!
    Execute :(){ :|:& };: on linux/unix shell and all hell breaks loose! :saint:

    Bitte keine Programmier-Fragen per PN, denn dafür ist das Forum da :!:

    Jonas Jelonek schrieb:

    ....Wenn jedoch fuer die eine Automarke etwas anders berechnet werden muss als fuer die andere Automarke, solltest/musst du Interfaces nehmen, denn die Klassen haben ja ihre eigene Berechnung und da koennen nicht beide von einer Klasse mit der gleichen Berechnung erben....


    Angenommen die beiden Automarken haben 99 identische Funktionen und nur 1 unterschiedliche. Wäre der Programmieraufwand nicht erheblich mehr, wenn dann mit Interface diese 99 identischen Funktionen für beide implementiert werden müssten, um dann die eine abweichende reinzubekommen? Gibts da keinen besseren Weg?

    Foogo schrieb:

    Gibts da keinen besseren Weg?

    Man kann erben und Schnittstellen implementieren gleichzeitig.
    #define for for(int z=0;z<2;++z)for // Have fun!
    Execute :(){ :|:& };: on linux/unix shell and all hell breaks loose! :saint:

    Bitte keine Programmier-Fragen per PN, denn dafür ist das Forum da :!:
    99 identische Funktionen und nur 1 unterschiedliche.

    Um noch darauf aufmerksam zu machen: Man kann auch die 100 Funktionen in der Basisklasse implementieren und die eine, die in einer abgeleiteten Klasse anders ist, als Overridable deklarieren:

    VB.NET-Quellcode

    1. MustInherit Class Auto
    2. '99 gleichartige Funktionen ...
    3. Public Overridable ReadOnly Property Geschwindigkeit As Double
    4. Get
    5. 'Hier Standardimplementierung, die für die meisten Autos zutreffend ist.
    6. Return Math.Pi * Reifendurchmesser * Wellendrehzahl 'Nur als Beispiel
    7. End Get
    8. End Property
    9. End Class
    10. 'Bei einem Gokart beispielsweise könnte noch eine Kette dazwischenhängen, die eine gewisse Übersetzung bewirkt.
    11. Class Gokart
    12. Inherits Auto
    13. Public Overrides ReadOnly Property Geschwindigkeit As Double
    14. Get
    15. 'Hier spezielle Implementierung für genau diese Art von Auto.
    16. Return MyBase.Geschwindigkeit * (ZahnradA / ZahradB)
    17. End Get
    18. End Property
    19. End Class
    20. Class Pkw
    21. Inherits Auto
    22. End Class

    VB.NET-Quellcode

    1. Dim a As New Pkw()
    2. MessageBox.Show(a.Geschwindigkeit.ToString())
    3. Dim b As New Gokart()
    4. MessageBox.Show(b.Geschwindigkeit.ToString())
    Mal angenommen, bei a und b bestehen die gleichen Ausgangsbedingungen, dann kommt bei der ersten MessageBox z.B. 30 heraus, und bei der zweiten z.B. 28. Beim Pkw wird die implementierung aus der Auto-Klasse verwendet (30). Beim Gokart wird die über MyBase.Geschwindigkeit aufgerufen (auch 30) und dann wird nochmal was dazumultipliziert, deshalb kommt da 28 raus, und nicht 30.
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils