Suche Tutorials/Beispiele zum Entwickeln von Datenbank-Applikationen

  • VB.NET

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

    Suche Tutorials/Beispiele zum Entwickeln von Datenbank-Applikationen

    Ich suche Tutorials oder Bücher, die ein komplettes Projekt von Anfang bis Ende MIT Erklärungen und Erläuterungen zeigen.

    Die meisten Tutorials beschränken sich auf das Veranschaulichen von sehr eingeschränkten Vorgängen. Man kann sich dann aus mehreren Tutorials die Informationen zusammen suchen, die man braucht, aber dabei wird dann nicht gezeigt, wie alles optimal nach allen regeln der Kunst zusammengeführt werden sollte.

    Beispiel Datenbankanbindung:
    Die meisten Tutorials und Beispiele sind so aufgebaut, dass in einer Methode die Verbindung zur Datenbank aufgebaut wird, Daten abgefragt werden, die Verbindung wieder geschlossen wird. Im Nächsten Beispiel ist wieder alles in einer Methode gemacht, nur dass Daten nicht abgefragt werden, sondern aktualisiert. Usw. Schlimmstenfalls sind das auch noch Konsolenanwendungen.

    Diese Tutorials und Beispiele sind gut, wenn man im Einzelnen wissen möchte, wie etwas funktio-niert.
    Wenn man das aber weiß, gilt es diese einzelnen Schritte zu einem Ganzen zusammen zu fügen. Ähnlich, wie das Spielen mit Legosteinen.

    Mir geht es dabei um die Strategien, die dabei angewendet werden.
    Um es noch mal mit den Legosteinen zu veranschaulichen, mich interessiert nicht der einzelne Stein, sondern die Art, wie sie am besten zusammen gesetzt werden, damit man am Ende ein schönes und stabiles Haus gebaut hat.
    Setzt man nämlich einfach nur Steine ohne Strategie zusammen, baut man vielleicht auch ein Haus, das wird aber sicher nicht so gut sein.

    Es wäre schön, wenn sich die Beispiele um das Erstellen von lokalen (kleinen) Datenbankanwendungen drehen (also ohne Datenbankserver, sondern nur mit einer File-Datenbank).
    Ich weiß zwar, wie man mit so einer Datenbank umgeht, ich kann auch so eine Applikation erstellen. Ich möchte aber sehen, wie andere diese Problemstellung lösen.
    Es gibt nur ein Buch das ich kenne das das behandelt was Du Dir vorstellst: Access 2007 Das Praxisbuch für Entwickler von André Minhorst und Sascha Trowitzsch.

    Allerdings für Access als Entwicklungsumgebung.

    Da es Dir aber weniger um Code-Beispiele geht, könnte es vielleicht Dir was bringen. Dort wird halt von A-Z die Entstehung einer Datenbank-Anwendung behandelt, allerdings ohne Berücksichtigung von OOP oder sonstigen Programmierprinzipien (logisch, wäre auch mit VBA etwas schwer umzusetzen ^^).

    Ansonsten bleibt Dir wohl nichts anderes übrig als für jede Problemstellung im Internet nach Lösungen zu suchen und diese dann selber zusammen zu bauen. Quasi die Bauteile einzeln suchen und dann selber rausbekommen wie man die am effektivsten zusammen setzt.

    Aber jetzt wo Du es so erwähnst ... ja Du hast Recht, sowas fehlt tatsächlich. Wäre mal eine Idee sowas umzusetzen, würde aber wohl ein riesen Aufwand werden und von daher wohl nur unter kommerziellen Aspekten machbar. ;)

    Gruß

    Rainer

    raist10 schrieb:

    Da es Dir aber weniger um Code-Beispiele geht, könnte es vielleicht Dir was bringen. Dort wird halt von A-Z die Entstehung einer Datenbank-Anwendung behandelt, allerdings ohne Berücksichtigung von OOP oder sonstigen Programmierprinzipien (logisch, wäre auch mit VBA etwas schwer umzusetzen ^^).


    Leider geht es mir genau darum. Wie man eine Datenbank an sich aufbaut, wie man auf die Daten zugreift, wie man sie manipuliert usw. weiß ich alles.

    Für mich ist die Strategie interessant, wie ich das alles optimal unter einen Hut in OOP bekomme.
    Man kann ein Projekt mit einem Formular erstellen, ein paar Buttons drauf packen, und direkt im Event-Handler der Buttons Datenbank-Aktionen durchführen. Das würde zwar auch irgendwie funktionieren, wäre aber nicht „die feine Art“.

    Schön ist es sicher, wenn man verschiedene Schichten (Presentation, Logik, Datenzugriff, usw) erstellt, so dass man bei Bedarf sogar die Datenbankart (z.B. von SQLite nach MS SQL Compact) wechseln kann, ohne das komplette Programm umbauen zu müssen.

    Kürzlich habe ich gelernt, dass man über die Attribute eines Daten-Objektes und mit Hilfe von Reflection die Datenbank inkl. Struktur erstellen kann (sehr interessant für so kleine lokale Datenbanken). Das habe ich vorher nicht gekannt.
    Und so gibt es bestimmt viele Tricks und Kniffe, wie man solche Sachen so implementiert, dass das Programm, schlank, performant und leicht erweiterbar ist.

    Es gibt halt viele Beschreibungen (Klasse SQLConnection macht dies und das, Klasse SqlDataReader macht das und das, …) und viele Beispiele, die auch nur kurz die Funktionsweise beschreiben. Es fehlt mir aber meistens an praxisnahen Beispielen, die schon in ein größeres Projekt integriert sind.

    Es ist zwar nützlich zu wissen, wie ein Automotor funktioniert, ich finde es dann aber auch interessant, wie er dann z.B. die Kraft auf die Räder überträgt, wie er angebracht ist, wie er Klimaanlage und Lichtmaschine antreibt, wieso er etwas so tut, wie er es tut, usw.

    raist10 schrieb:

    Aber jetzt wo Du es so erwähnst ... ja Du hast Recht, sowas fehlt tatsächlich. Wäre mal eine Idee sowas umzusetzen, würde aber wohl ein riesen Aufwand werden und von daher wohl nur unter kommerziellen Aspekten machbar. ;)


    Gäbe es ein solches (gutes) Buch, würde ich mit Freuden dafür zahlen. Das gilt nicht nur für das Gebiet Datenbanken, sondern auch für viele andere Gebiete.
    Naja, MyLayerPattern habichdir ja schonmal skizziert (inklusive Strategy-Pattern) - aber du schienst unbedingt auf etwas komplizierteres aus zu sein. ;) (Ähm - hast du dein Rezeptbuch mittm Schichtenmodell eiglich fertig-bekommen?)

    Vermutlich hat raist recht, wenner gleich einwendet, dass dieser Ansatz begrenzt ist (nämlich - k.A. - ab so 50 Tabellen wirds allmählich ungemütlich mit einem Dataset). Ist mit so vielen zu rechnen? (weil ich hatte im Leben noch nicht mehr als 15).
    Und ich bin nachwievor der Ansicht, dass man erst dann eine höhere Architektur-Stufe erklimmen kann, wenn man tatsächlich eine Anforderung vorliegen hat, die eine entsprechend aufwändige Architektur auch erfordert. Yagni heißt dieses Programmierprinzip.

    Edit: google mal nach AdventureWorks - das ist als aufwändiges Lehrstück konzipiert (war aber damals c#2003 - k.A., obs geupdated wurde).

    ErfinderDesRades schrieb:

    (Ähm - hast du dein Rezeptbuch mittm Schichtenmodell eiglich fertig-bekommen?)


    Ich bin mitten drin. Komme derzeit leider viel zu selten dazu, da mein erstes Hobby derzeit viel Zeit fordert.

    Das ist aber auch der Grund, weswegen ich frage. Ich habe mir Strategien überlegt und auch teilweise schon umgesetzt. Das bedeutet aber nicht, dass die gut sind. Daher wollte ich eine Art „Vergleich“, ob ich nicht auf dem Holzweg bin.

    ErfinderDesRades schrieb:

    Und ich bin nachwievor der Ansicht, dass man erst dann eine höhere Architektur-Stufe erklimmen kann, wenn man tatsächlich eine Anforderung vorliegen hat, die eine entsprechend aufwändige Architektur auch erfordert. Yagni heißt dieses Programmierprinzip.


    Dieses Prinzip geht davon aus, dass man zu viel macht. Es gibt aber auch noch den Fall, dass man zu wenig oder das Falsche macht.
    Angenommen, ich mache meine Applikation fertig, verbreite sie, und setze sie ein. Irgendwann soll es um diverse Features erweitert werden.
    Ich möchte dann vermeiden, dass es nachher heißt „Ach hätte ich das am Anfang so, und nicht so aufgezogen, dann müsste ich nun nicht alles umbauen…“.

    Bei meinem aktuellen Projekt gehe ich von der Annahme aus, dass ich diverse Teile, die ich jetzt im Zuge der Entwicklung der Applikation, mit entwickle, auch wieder in anderen Projekten nutzen kann.
    So habe ich z.B. einen DAL (Data-Abstraction-Layer) als eigenes Projekt erstellt, welches ich in mein Hauptprojekt einbinde. Dieser Layer liefert nur Objekte, die aus dem FW direkt kommen, also z.B. DataTables, DataSets, usw. so dass der Applikation selbst egal ist, ob da nun SQLite oder sonst was für eine Datenbank drunter liegt.
    Wenn ich die Datenbank nun tauschen möchte, reicht es lediglich den DAL anzupassen, und alle Projekte, die diesen DAL nutzen, funktionieren weiterhin, ohne dass ich da auch nur eine Zeile Code ändern müsste.

    Angenommen, mein aktuelles Projekt liest nur Daten aus, manipuliert sie aber nicht. Dann müsste ich nach Yagni auch nur die Methoden zum Holen der Daten in meinem DAL implementieren. Oder direkt komplett auf den DAL verzichten und direkt in der Applikation ohne Abstraktion auf die DB zugreifen.
    In beiden Fällen hätte ich bei weiteren Projekten deutlich mehr Arbeit, weil ich ja jedes Mal den krempel neu machen müsste. Bei einer Änderung z.B. der Datenbank müsste ich auch jedes Projekt anpacken.

    Ich mache mir lieber stattdessen ein Mal viel Arbeit, und spare dann viel mehr Arbeit in der Folge.
    Jo, das ist jetzt die Nagelprobe für dein Dal: Isses wirklich so flexibel, dasses dir bei Wiederverwendung Arbeit erspart, oder ist die Anpasserei ungebührlich aufwändig?
    Unterstützt es typisierte Datenklassen?

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

    @ GambaJo

    Jein, es ist nicht immer (meistens eigentlich nie) sich heute schon einen Kopf über zukünftige Anforderungen zu machen. Das YAGNI Prinzip ist schon richtig.

    Weisst Du heute schon bereits ob Deine Anwendung jemals mit einem anderen DBMS laufen wird? Vermutlich nein. Wird es das nie hast Du Dir heute richtig viel Aufwand gemacht für nichts. Bei einem Auftrag zahlt das kein Kunde.

    Anderseits wenn Du in 3 oder 4 Jahren dann doch ein anderes DBMS einsetzt kannst Du Dich da wirklich freuen heute so vorrausschauend agiert zu haben? Vermutlich auch nicht, weil dann eh alles für die Katz war da sich in der Zeit die Technik so verändert hat, dass das was Du heute gemacht hast eh keine Schuß Pulver mehr wert ist.

    Gerad bei Produkten und Techniken die heute von MS priorisiert werden. Wer unter 2005 oder 2008 sich voll auf Linq-to-SQL gestürtzt hat, ist heute der Depp da MS mal so nebenbei beschlossen hat die Technik einzustampfen und sich voll auf ADO NET (was auch schonmal tot gesagt war) zu stürzen. Ich würde ja fast drauf wetten das in ein paar Jahren keine Sau mehr mit OleDB arbeitet sondern mit dem Nachfolgermodell das dann viel besser, schöner, neuer und schneller ist.

    Es gibt schon einen Grund wieso etwas ältere/konservative DB'ler (wie ich auch einer bin) noch klassisch mit der alten OCDB-Connection arbeiten. Ist zwar alt, aber funktioniert 100 pro zuverlässig. Die neuen Techniken wie z.B. typisiertes DataSet machen es ja nur mit riesem Aufwand möglich das DBMS zu wechseln und man kann nicht bidirektional fahren (zwei unterschiedliche DBMS mit dem gleichen typisierten DS ansprechen). Mit OCDB völlig latte, einfach nur passenden Connection-String übergeben und es [fast] schnuppe ob da ein MSSQL-, Oracle- oder MySQL-Server hinter hängt.

    Du kannst also quasi schon das YANGI-Prinzip einhalten und aber auch die Flexibilität im Nachgang Dein Projekt zu evolutionieren ohne heute unnötigen Aufwand zu betreiben. Es ist einfach die Wahl der richtigen Technik und des richtigen Objektmodells. Schränkt mich ein Objektmodell zu sehr ein, nehme ich halt ein Objektmodell was mich flexibler macht und gut ist.

    Dann richtiges OOP und Programmierung gegen Schnittstelle anstatt Implementierung und schon ist der Lack fertig in dem Sinne das Du heute geringst möglichen Aufwand hattes, aber im Nachgang unendlich flexibel bist. Objekte/klassen die gegen Interface geproggt sind können ja problemlos an beiden Enden der Schnittstelle ausgetauscht/angepasst werden auf die neuen Anforderungen.

    Streng genommen sind die Hauptgründe wieso man richtig OOP proggt: Sehr gute Wartbarkeit und absolute Flexibilität bei Erweiterung/Ausbau. Wenn man dann noch sauber kapselt und zu 100% redunanten Code vermeidet dann kann man nachträgliche Anpassungen erst vornehmen wenn sie wirklich gefordert sind und hat keinen zusätzlich Aufwand durch Re-Design des alten Codes. Das ist die wahre Flexibilität und der Punkt der zählt und das wiederspricht sich eben in keinster Weise mit dem YAGNI-Prinzip sondern passt zu 100% damit zusammen.

    Wäre zumindest mal so meine naive Sichtweise der Dinge. ;)

    @ ErfinderDesRades


    Vermutlich hat raist recht, wenner gleich einwendet, dass dieser Ansatz begrenzt ist (nämlich - k.A. - ab so 50 Tabellen wirds allmählich ungemütlich mit einem Dataset)


    Ich werde die Diskussion hier nicht nochmal los treten. ^^

    Anderseits wie oben schon geschrieben verbieten sich typisierte DataSets schon fast automatisch wenn man Flexibilität bei der Wahl des DBMS haben will. Hauptproblem hierbei ist eben das jedes DBMS mit unterschiedlichen Datentypen arbeitet ... eigentlich völlig schizophren das man da sich noch nicht aneinander angeglichen hat, bzw. einen Datentypen-Standard für DBMS geschaffen hat.

    Aber vermutlich will man die DB-User auch an das eigene DBMS binden und von daher wäre eine Datentypen-Standarisierung nachteilig weil man damit dann problemlos das DBMS wechseln könnte. ^^

    Gruß

    Rainer

    ErfinderDesRades schrieb:

    Jo, das ist jetzt die Nagelprobe für dein Dal: Isses wirklich so flexibel, dasses dir bei Wiederverwendung Arbeit er-spart, oder ist die Anpasserei ungebührlich aufwändig?
    Unterstützt es typisierte Datenklassen?


    Nein. Wie gesagt, so weit bin ich noch nicht.
    Hatte aber eigentlich auch nicht vor typisierte DataSets zu nutzen.
    Raist10 hat meine Gründe schon ganz gut zusammengefasst. Zumal typisierte DataSets dann ja noch mehr Overhead generieren würden.

    raist10 schrieb:

    Jein, es ist nicht immer (meistens eigentlich nie) sich heute schon einen Kopf über zukünftige Anforderungen zu machen. Das YAGNI Prinzip ist schon richtig.

    Weisst Du heute schon bereits ob Deine Anwendung jemals mit einem anderen DBMS laufen wird? Vermutlich nein. Wird es das nie hast Du Dir heute richtig viel Aufwand gemacht für nichts. Bei einem Auftrag zahlt das kein Kunde.


    Ja, das zahlt kein Kunde. Deshalb versuche ich es ja privat ein wenig zu ergründen.
    Ich mache mir keine festen Gedanken über zukünftige Anforderungen, ich möchte mir nur nicht selbst die Tür zunageln und zumauern. Wer weiß, was noch so kommt.

    Ich möchte halt nicht redundanten Code generieren, so dass ich bei Problemen den Fehler genau an nur einer Stelle korrigieren muss.
    Und ich denke, dazu gehört eine gewisse Abstraktion, Abkapselung und Granularität, also wie Du schon sagtest, ein cleveres Objektmodell.
    Und das würde ich mir jetzt im Vorfeld gerne ein Mal richtig zur Brust nehmen.

    raist10 schrieb:


    Anderseits wenn Du in 3 oder 4 Jahren dann doch ein anderes DBMS einsetzt kannst Du Dich da wirklich freuen heute so vorrausschauend agiert zu haben? Vermutlich auch nicht, weil dann eh alles für die Katz war da sich in der Zeit die Technik so verändert hat, dass das was Du heute gemacht hast eh keine Schuß Pulver mehr wert ist.


    Ja, aber einen Tod muss man sterben. Ich denke, es ist nicht praktikabel immer der neuesten gerade modernen Technologie nachzulaufen. Dann ist man ja nur noch mit Implementieren beschäftigt.
    Wenn die Anwendung mal steht, und längere Zeit genutzt wird, und es dann irgendwelche neuen Techniken gibt, die auch große Vorteile bringen, dann denke ich, kann man die alte Technik über Bord werfen und ersetzen.

    raist10 schrieb:


    Dann richtiges OOP und Programmierung gegen Schnittstelle anstatt Implementierung und schon ist der Lack fertig in dem Sinne das Du heute geringst möglichen Aufwand hattes, aber im Nachgang unendlich flexibel bist. Objekte/klassen die gegen Interface geproggt sind können ja problemlos an beiden Enden der Schnittstelle ausgetauscht/angepasst werden auf die neuen Anforderun-gen.


    Das mache ich jetzt auch immer mehr. Früher konnte mir niemand wirklich plausibel sagen, wofür Schnittstellen gut sind. Mir erschien das immer unsinnig sich einfach nur ein Muster für eine Kasse erstellen zu müssen. Die Klasse kam ja auch ohne gut aus.
    Es sah für mich immer nach unnötigem Ballast aus. Fast alle Bücher behandeln das Thema auch sehr stiefmütterlich. Es wird beschrieben, dass es so etwas gibt, wie man es aufbaut und wie nutzt. Aber der tatsächliche Grund oder besser gesagt die Vorteile davon werden kaum (verständlich) erklärt.
    Erst das Buch „Entwurfsmuster“ hat mir die Augen geöffnet. Ich muss zugaben, alles darin habe ich noch nicht verstanden, aber ich verstehe nun, warum Schnittstellen der Vererbung vorzuziehen sind.
    Sehr interessanter Ansatz.
    Aber so, wie ich das verstehe, zeigt sich die Stärke von EBC erst richtig, wenn man viel asynchron arbeitet, oder? Da ja durch dieses Modell die Thread-Sicherheit automatisch gewährleistet ist.

    Ich weiß nicht, ob das immer so ein muss, aber bei deinem Beispiel ist mir aufgefallen, dass alle benötigten Atome-Objekte direkt zu Anfang instanziiert und verknüpft werden.
    Bei einer komplexeren Anwendung, als in deinem Beispiel stelle ich mir das nicht so optimal vor.
    mit Code-Injection kenn ich mich nich soo aus, aber ich glaub, da muß man die Objekte ebenfalls gleich anfangs instanzieren, damit man sie über den Konstruktor in die Komponenten geben kann - also wennichdas recht verstande habe, käme das aufs selbe raus.

    Dasses grad mit Threading so schön aufgeht, ist eiglich nur ein Nebeneffekt, weil Delegaten halt von Haus aus Threading nach dem asynchronen Entwurfsmuster mitbringen.
    Ist bei so Download-Geschichten natürlich grad passend.

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

    ErfinderDesRades schrieb:

    IMO sind Schnittstellen als Architektur-Verträge auch ein Modell von gestern. Mit EBC lässt sich wesentlich besser entkoppelt programmieren, und zwar ohne den schnittstellen-Ballast.


    Ich muss ehrlich zugeben das ich mich mit EBC noch so gut wie gar nicht auseinander gesetzt habe. Die Logik dahinter erscheint mir zwar richtig, aber ich sehe irgendwo noch nicht den großen Vorteil gegenüber der Interface-Konzeption, vor allem da ja auch EBC - zumindest dem Vordenker des Modells nach - auch über/per Schnittstellen funktioniert.

    Aber habe mich vermutlich nur noch nicht eingehend genug damit beschäftigt. Hast Du vielleicht mal irgendwo ein simples Beispiel in VB.NET dazu? Oder besser noch kannst Du mal ein simples Beispiel analog zu dem Code den ich als Beispiel in dem Thread zum Thema Mixin-Klassen in Post 6 gezeigt habe erstellen?

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

    @ GambaJo


    Ja, aber einen Tod muss man sterben. Ich denke, es ist nicht praktikabel immer der neuesten gerade modernen Technologie nachzulaufen. Dann ist man ja nur noch mit Implementieren beschäftigt.
    Wenn die Anwendung mal steht, und längere Zeit genutzt wird, und es dann irgendwelche neuen Techniken gibt, die auch große Vorteile bringen, dann denke ich, kann man die alte Technik über Bord werfen und ersetzen.


    Natürlich ist es nicht sinnvoll jeder neuen Technik hinter her zu laufen und andauernd nur mit Redesign seiner Anwendungen auf die neue Technik beschäftigt zu sein.

    Eine alte Technik würde ich z.B. nur über Bord werfen/Redesignen wenn ich mit ihr entweder kontinuierlich Probleme habe oder anstehende Funktionserweiterungen mit der neuen Technik besser oder überhapt nur damit zu realisieren wären. Solange da nichts ansteht würde ich keinen Code Redesignen. Also ohne Not/Zwang fasse ich keinen bestehenden Code der ohne Probleme läuft.


    ... aber ich verstehe nun, warum Schnittstellen der Vererbung vorzuziehen sind.


    So würde ich das jetzt nicht pauschal formulieren.

    Gerade wenn es um das Thema gleiche Basis für viele verschiedene Klassen geht hat Vererbung durch noch Vorteile die ich auch nicht missen wollte.

    Wenn ich mal das Beispiel aus dem anderen Thread (Thema Mixin Klassen in VB.NET) aufgreife bietet sich für die Fahrzeugklassen eine Vererbung über mehrere Ebenen hinweg einfach an. Hier mal als simples Beispiel worauf ich hinaus will:

    MasterClass: Fahrzeuge, abstrakte Klasse

    Child-Classes Level 1:

    erbt die MasterClass

    - Landfahrzeuge
    Implements ILandbewegungen

    - Luftfahrzeuge
    Implements IFlugbewegungen

    - Wassfahrzeuge
    Implements IWasserbewegungen

    Child-Classes Level 2:

    erbt von der entsprechenden Child-Class aus Level 1

    - motorisierte Landfahrzeuge
    Implements IMotor
    - nicht motorisierte Landfahrzeuge

    - motorisierte Luftfahrzeuge
    Implements IMotor
    - nicht motorisierte Luftfahrzeuge

    - motorisierte Wasserfahrzeuge
    Implements IMotor
    - nicht motorisierte Wasserfahrzeuge

    Usw. und so fort.

    Somit sorgt jede Child-Ebene dafür das bereits die Funktionen beinhalten sind, die für alle weiteren Child-Klassen gleich sind, bzw. gelten müssen.

    Am Ende hast Du eine Pyramide und die unterste Ebene sind dann die Klassen die einen einzelnen Fahrzeugtyp representieren. Dadurch hast Du für jedes Fahrzeug was am unteren Ende steht ein starres Korsett was vorhanden sein muss und was an Funktionen vorhanden sein muss. Und das ist letztendlich Sinn und Zweck der Übung.

    Ich würde daher also nicht generell sagen das das Interface der Vererbung vor zu ziehen ist, bloss die Fälle wo Vererbung sinnvoller/besser ist sind eher begrenzt.

    Gruß

    Rainer

    raist10 schrieb:

    Ich muss ehrlich zugeben das ich mich mit EBC noch so gut wie gar nicht auseinander gesetzt habe. Die Logik dahinter erscheint mir zwar richtig, aber ich sehe irgendwo noch nicht den großen Vorteil gegenüber der Interface-Konzeption, vor allem da ja auch EBC - zumindest dem Vordenker des Modells nach - auch über/per Schnittstellen funktioniert.

    Aber habe mich vermutlich nur noch nicht eingehend genug damit beschäftigt. Hast Du vielleicht mal irgendwo ein simples Beispiel in VB.NET dazu? Oder besser noch kannst Du mal ein simples Beispiel analog zu dem Code den ich als Beispiel in dem Thread zum Thema Mixin-Klassen in Post 6 gezeigt habe erstellen?

    also der große Vorteil ist, dass Interfaces entbehrlich sind als Verträge zwischen Komponenten einer Anwendung.
    In komplexen Anwendungen können die Verträge zwischen den Komponenten selbst wieder ziemlich kompliziert werden.

    Angenommen eine Komponente verwaltet Items, und bietet die Methoden IndexOf(itm As T) As Integer und Sub RemoveAt(index As Iteger)
    Nun braucht man die Klasse in verschiedenen Komponenten, und will sich auch die Möglichkeit offenhalten, die Komponente auszutauschen.
    Nach Interface-Logik würde man ein Interface erfinden, etwa so

    VB.NET-Quellcode

    1. Public Interface IDataClass
    2. Function IndexOf(itm As T) As Integer
    3. Sub RemoveAt(index As Iteger)
    4. End Interface

    Eine andere Komponente, die die Klasse nutzt, benötigt eigentlich nur das IndexOf(). Schon bisserl unschön, Verstoß gegen Kapselung, denn das Interface IDataClass teilt dieser Klasse neben dem Member IndexOf() auch den Member RemoveAt() mit, der sie gar nichts angeht.

    Und natürlich der Code-Aufwand:
    Komponente A muß die dll mit der Interface-Deklaration eingebunden haben, ebenso Komponente B und die Datenklasse natürlich auch. Also eine Entkopplung bedeutet gewissermaßen eine Verdopplung der Klasse, und es ist fast ebensoviel gekoppelt, nur halt gegen Interfaces anstatt direkt gegen die anneren Klasse.
    Und das bringt natürlich mehr Wartungsarbeit, wenn aussen-sichtbare Veränderungen anne DataClass vorzunehmen sind.

    EBC hat auch höheren Codeaufwand, im Vergleich zu vollverkoppelten Anwendungen. Aber zB. die Interface-Dlls, die an vielen Stellen eingebunden sein müssen (und manchmal eben auch nicht) - entfallen.

    Statt dass die NutzerKlasse das Interface IDataClass kennen muß, mit dem Member .IndexOf, hat sie einen Delegaten IndexOf As Func(Of T, Integer), den sie feuert. Und einen Delegaten DataRemoveAt As Action(Of Integer).
    Und die Komponente B hat nur den IndexOf-Delegaten, weil sie halt RemoveAt nicht braucht. (Und wenn sie ihn braucht, kann sie ihn ja kriegen.)
    Das Multi-Tut ist eigentlich bereits eine sehr simple BeispielAnwendung von EBC - simpler gehts kaum.

    Mit deinem Beispiel habich Verständnisprobleme. Weil die Fahrzeuge ja alle dieselbe Bewegung-"Eigenschaft" haben, und du mixst Interface-Extension-Vererbung mit klassischer Vererbung zusammen, und die Bewegung ist auch nochmal eine Klasse, und ich denk: "Warum ist Bewegung nicht eine Property der Basis-Fahrzeug-klasse, und alle anneren Fahrzeuge erben halt davon - also ganz klassisch und gut ist?"

    Ich glaub auch garnet, dass die MixIn-Klassen-Idee überhaupt was mit Entkopplung von Komponenten in komplexen Anwendungen zu tun hat.

    Von daher würde ein EBC-Beispiel sich vlt. besser mit was annerem beschäftigen, und man bräuchte halt zum Vergleich eine per Interface entkoppelte Anwendung.

    ErfinderDesRades schrieb:


    also der große Vorteil ist, dass Interfaces entbehrlich sind als Verträge zwischen Komponenten einer Anwendung.
    In komplexen Anwendungen können die Verträge zwischen den Komponenten selbst wieder ziemlich kompliziert werden.


    Ist das wirklich ein Vorteil? Mir gefällt gerade das Konzept des Vertrages. Das macht es unproblematischer Komponenten auszutauschen, weil ja auch die neue Komponente den Vertrag erfüllen muss. Und erfüllt sie den Vertrag muss man sich nicht um mögliche negative Auswirkungen kümmern, da die ausgeschlossen sind.


    Eine andere Komponente, die die Klasse nutzt, benötigt eigentlich nur das IndexOf(). Schon bisserl unschön, Verstoß gegen Kapselung, denn das Interface IDataClass teilt dieser Klasse neben dem Member IndexOf() auch den Member RemoveAt() mit, der sie gar nichts angeht.


    Hier greift ja quasi der Vorteil des Mixin-Konstruktes, bei dem man eben nicht alle Methoden im Interface deklariert, sondern Methoden als Extensions zur Verfügung stellt und somit die nutzende Klasse nicht zwingend alle Methoden implementieren muss, sondern sich der Methoden bedient die sie wirklich braucht. Gut ... Nachteil ist hier das bei IntelliSense alle Methoden der Interface-Extensions angezeigt werden auch wenn man sie in der Klasse nicht braucht.


    Statt dass die NutzerKlasse das Interface IDataClass kennen muß, mit dem Member .IndexOf, hat sie einen Delegaten IndexOf As Func(Of T, Integer), den sie feuert. Und einen Delegaten DataRemoveAt As Action(Of Integer).
    Und die Komponente B hat nur den IndexOf-Delegaten, weil sie halt RemoveAt nicht braucht. (Und wenn sie ihn braucht, kann sie ihn ja kriegen.)


    Und genau dieses feuern ist mein Problem, bzw. das Thema an dem ich gedanklich hänge und wieso ich mir das Konzept auch noch nicht wirklich intensiv angeguckt habe. Denn wenn eine Klasse feuert muss es ein Objekt geben das abhört, gibt es das nicht passiert genau nix.

    Somit kann ein Objekt nur über die "Verbindungsstelle" Response-Objekt funktionieren. Und diese Stelle muss verwalten was gefeuert wird und was wozu gehört. Z.B. zwei Objekte die parallel instanziert sind feuern beide die gleiche Request-Anfrage nur mit unterschiedlichen Parametern, dann muss jede Klasse davon eine eigene Instanz des Response Objektes halten und das Response Objekt muss entscheiden wohin umgeleitet wird und davon die Instanz halten bis die Antwort erfolgt die dann wieder zurück geleitet wird per Delegaten.

    Da die beiden agierenden Klassen nichts voneinander wissen und das Response Objekt nur Delegaten weiterleitet/empfängt stellt sich für mich die große Frage ... wo ist der bindende Vertrag an Hand dessen eine Klasse sich darauf verlassen kann das auf der Gegenseite die entsprechende Methode auch vorhanden ist?


    Das Multi-Tut ist eigentlich bereits eine sehr simple BeispielAnwendung von EBC - simpler gehts kaum.


    Eigentlich weniger ... da es für mich extrem schwer nachzuvollziehen ist. Was ja auch vielfach als große Schwäche von EBC angesehen wird.

    Aber gut ... ich werde mal gucken ob ich die Tage Lust und Laune bekomme mich mal da rein zu knien. Daher ja auch meine Anfrage hier auf ein simples Beispiel das vllt aus nur 2 einfachst Klassen besteht und demonstriert wie die nun genau miteinander kommunizieren, bzw. eben die Kommunikation über das Response-Objekt verdeutlichen.


    Mit deinem Beispiel habich Verständnisprobleme. Weil die Fahrzeuge ja alle dieselbe Bewegung-"Eigenschaft" haben, und du mixst Interface-Extension-Vererbung mit klassischer Vererbung zusammen, und die Bewegung ist auch nochmal eine Klasse, und ich denk: "Warum ist Bewegung nicht eine Property der Basis-Fahrzeug-klasse, und alle anneren Fahrzeuge erben halt davon - also ganz klassisch und gut ist?"


    Wie schon im Thread gesagt, die Schnittstelle IBewegung war unglücklich gewählt da diese so wie sie da steht tatsächlich gut in die MasterClass passen würde. Aber stell Dir das Beispiel mit zwei Bewegungs-Schnittstellen vor: IMotorisierteBewegung und IUnmotorisierteBewegung. Dann bekommt das ganze gleich eine komplett andere Bedeutung. Denn diese können nicht mehr in der MasterClass Fahrzeuge implementiert werden, sondern müssen jetzt direkt in die Fahrzeuge rein da ja Auto eben eine motorisierte Bewegung hat und Fahrrad eben eine unmotorisierte Bewegung.

    Ich habe natürlich nur eine einfache Rückgabe gewählt ala "Ich bewege mich", die sollte aber stellvertrend für komplexere Berechnungsmechanismen stehen ... z.B. Berechnung der Bewegungsgeschwindigkeit oder Beschleunigungsberechnungen. Also nix was einer einfachen Property-Rückgabe entspricht sondern wirklich Berechnungsmethoden die man grundsätzlich auslagern würde da ja die Berechnungsmethoden für alle Fahrzeuge gleich sind, sich nur die Variablen wie Gewicht, Motorkraft, Luftwiederstand u.ä. verändern. Diese Dinge zu berechnen ist nicht Aufgabe der Klasse Auto oder Flugzeug oder Fahrrad, sondern eben die Aufgabe einer eigenen Klasse.


    Ich glaub auch garnet, dass die MixIn-Klassen-Idee überhaupt was mit Entkopplung von Komponenten in komplexen Anwendungen zu tun hat.


    Wieso nicht? Gerade die Thematik mit den Mixin-Klassen (bzw der Nachbildung derselben) erlaubt es ja problemlos die jeweiligen Gegenseiten des Interfaces in x-entkoppelten Komponenten vor zu halten. Quasi kann jede Extension eines einzigen Interface auf unterschiedliche Komponenten zu greifen. Natürlich ist das einfachst Beispiel auch nicht wirklich komplett, da man durchaus überlegen würde ob man der aufgerufenen Gegenseite des Interfaces nicht noch ein eigenes Interface spendieren würde ... also quasi Programmierung Klasse -> Interface = Interface <- Klasse.

    Oder reden wir hier aneinander vorbei? Entkoppelung von Komponenten heisst für mich schlicht die Aufteilung der Komponenten auf kleine gut wartbare externe Häppchen (z.B. Dll's).

    Gruß

    Rainer

    raist10 schrieb:

    Mir gefällt gerade das Konzept des Vertrages. Das macht es unproblematischer Komponenten auszutauschen, weil ja auch die neue Komponente den Vertrag erfüllen muss. Und erfüllt sie den Vertrag muss man sich nicht um mögliche negative Auswirkungen kümmern, da die ausgeschlossen sind.
    Statt des Vertrages hast du bei EBC einen Delegaten, mit einer klar definierten Signatur.
    Das ist dann kein Vertrag, sondern eine Steckbuchse: Was da reinpasst, passt da rein, und anneres eben nicht. Gewissermaßen die physikalische Form von Stecker und Buchse machen einen zusätzlichen Verbindungsvertrag üflüssig.
    Und genau dieses feuern ist mein Problem.
    Denn wenn eine Klasse feuert muss es ein Objekt geben das abhört, gibt es das nicht passiert genau nix.

    Ja, so isses - ist doch in Ordnung. Man kann sogar konfigurieren, ob dann eine Exception fliegen soll, oder ob nichts passieren eben i.O. ist. Ein solcher Delegat

    VB.NET-Quellcode

    1. Public IndexOf As Func(Of T, Integer) = Nothing
    schmeißt dir eine Exception, wenn da IndexOf() aufgerufen wird, ohne dasses abonniert ist.
    Hingegen

    VB.NET-Quellcode

    1. Public IndexOf As Func(Of T, Integer) = Sub
    2. End Sub
    nicht - der führt nur nix aus.

    Zum Beispiel: Ich denk, da muß man wirklich was anneres konzipieren. ZB auch mitte Geschwindigkeitsberechnung - ich täte denken, da sich die Berechnungen der verschiedenen Fahrzeuge unterscheiden, brauchts eine MustOverride GetSpeed() as Double in der Basisklasse, und alle Fahrzeuge müssen die überschreiben, und jeweils ihren speed korrekt ausrechnen - grad in dem Beispiel ist mir wenig plausibel, wie man diese Berechnung iwie dranmixen sollte. Weil beim Fahrad ist die Steigung entscheidend, und beim Flugzeug die Windgeschwindigkeit und Richtung, und fürs Auto vlt diverse Verkehrsschilder.

    Und wie gesagt: ich sehe nicht, was das mit Entkopplung zu tun hat. MixIn scheint mir ein fabelhaft praktisches Konzept zu sein, um neue Objekte zusammenzukomponieren, ohne in deren SourceCode einzugreifen. Hat natürlich logisch die Beschränkung, dasses, wie bei Extensions überhaupt, nur um Public Member gehen kann.

    Aber entkoppeln würde heißen: ich baue einen Motor ins Auto, und der Fahrer kennt den nicht, weiß nichtmal, dasses sowas ühaupt gibt. Er hat nur sein Gaspedal, mit dem er die Vorwärtskraft einstellt.

    Also ich hab jetzmal sowas gebastelt:

    VB.NET-Quellcode

    1. Namespace Coupled
    2. Public Class Benziner
    3. Public ReadOnly Strength As Double = 120 'in PS ;)
    4. Private _Effort As Double
    5. Public Property Effort As Double
    6. Get
    7. Return _Effort
    8. End Get
    9. Set(ByVal value As Double)
    10. _Effort = Math.Min(value, Strength)
    11. Console.WriteLine("Brumm! (" & value & " PS)")
    12. End Set
    13. End Property
    14. End Class
    15. Public Class Auto
    16. Private _Motor As New Benziner
    17. Public Sub GibGas(ByVal value As Double)
    18. _Motor.Effort = value
    19. End Sub
    20. End Class
    21. Public Module Program
    22. Public Sub Main()
    23. Dim auto As New Auto()
    24. auto.GibGas(33)
    25. End Sub
    26. End Module
    27. End Namespace
    28. Namespace WithInterface
    29. Public Interface IMotor
    30. Property Effort As Double 'Gaspedal zur Einstellung der Kraftanstrengung
    31. End Interface
    32. Public Class Benziner : Implements IMotor
    33. Public ReadOnly Strength As Double = 120 'in PS ;)
    34. Private _Effort As Double
    35. Public Property Effort As Double Implements IMotor.Effort
    36. Get
    37. Return _Effort
    38. End Get
    39. Set(ByVal value As Double)
    40. _Effort = Math.Min(value, Strength)
    41. Console.WriteLine("Brumm! (" & value & " PS)")
    42. End Set
    43. End Property
    44. End Class
    45. Public Class Auto
    46. Private _Motor As IMotor
    47. Public Sub New(ByVal Motor As IMotor)
    48. _Motor = Motor
    49. End Sub
    50. Public Sub GibGas(ByVal value As Double)
    51. _Motor.Effort = value
    52. End Sub
    53. End Class
    54. Public Module Program
    55. Public Sub Main()
    56. Dim motor As New Benziner
    57. Dim auto As New Auto(motor)
    58. auto.GibGas(33)
    59. End Sub
    60. End Module
    61. End Namespace
    62. Namespace EBC
    63. Public Class Benziner
    64. Public ReadOnly Strength As Double = 120 'in PS ;)
    65. Private _Effort As Double
    66. Public ReadOnly EffortSet As Action(Of Double) = _
    67. Sub(value As Double)
    68. _Effort = Math.Min(value, Strength)
    69. Console.WriteLine("Brumm! (" & value & " PS)")
    70. End Sub
    71. Public ReadOnly EffortGet As Func(Of Double) = Function() _Effort
    72. End Class
    73. Public Class Auto
    74. Public EffortSet As Action(Of Double)
    75. Public Sub GibGas(ByVal value As Double)
    76. EffortSet.Invoke(value)
    77. End Sub
    78. End Class
    79. Public Module Program
    80. Public Sub Main()
    81. Dim motor As New Benziner
    82. Dim auto As New Auto
    83. motor.EffortSet.Handles(auto.EffortSet)
    84. End Sub
    85. End Module
    86. End Namespace


    Der Coupled-Namespace ist natürlich am einfachsten, nur leider ists Auto festgelegt auf Benziner-Motor.
    Die Auto-Klasse kann nicht unabhängig von der Benziner-Klasse entwickelt werden.

    Im WithEvents-Namespace muß Main Auto und Benziner kennen, und mittelbar auch IMotor. Auto muß IMotor kennen, und Benziner muß auch IMotor kennen.
    Die Auto-Klasse kann unabhängig von der Benziner-Klasse entwickelt werden, solange niemand am IMotor-Interface herumfummelt.

    im EBC-Namespace muß Main Auto und Benziner kennen, sonst kennt keiner keinen.
    Die Auto-Klasse kann unabhängig von der Benziner-Klasse entwickelt werden.


    Wie da jetzt die MixIn-Idee was einbringt - wie gesagt: k.A. Die Motorleistung erhöhen ist IMO weniger was zum Auslagern ausserhalb des Motors, sondern grade etwas, was als Private gekapselt gehört - all diese dreckigen Ventile, Motoröl, Hitze, Ruß und Gestank - PfuiDeibel! ;)
    @ ErfinderDesRades

    Danke für Dein Beispiel, allerdings ... es bingt mir gerade gar nichts weil es nicht funzt und ich auch gerade keinen Plan habe wie es zum Laufen bringen soll. Liegt vermutlich an meiner nicht detaillierten Kenntnis von Delegaten.

    Moniert wird das hier:

    VB.NET-Quellcode

    1. Public Sub Main()
    2. Dim motor As New Benziner
    3. Dim auto As New Auto
    4. motor.EffortSet.Handles(auto.EffortSet)
    5. End Sub


    Die 3te Zeile mit dem Hinweis das "Handles" kein Member von Motor.EffortSet ist.

    Mir ist zwar klar was da passieren soll, nämlich dem Delegaten EffortSet von Auto den Delegaten EffortSet von Motor als Ausführungs-Target zu zu weisen.

    Aber auch nach einem eigenen Umbau in Deinem Code und dem Versuch die Addresse direkt zu zu weisen (ala "Auto.EffortSet = AddressOf Motor.EffortSet") will es nicht laufen da der Compiler die Zuweisung mit dem Hinweis "Der AddressOf Operand muss dem Namen einer Methode entsprechen" moniert.

    Erst wenn ich in der Klasse Benziner EffortSet umbaue von Delegaten zu einer einfach Sub akzeptiert der Compiler die Zuweisung. Offensichtlich akzeptiert mein VS2010 Compiler keine Zuweisung eines Delegaten an einen Delegaten als Ausführungsziel. Wobei ich jetzt aber eben nicht glaube das mein Umbau damit es funzt dem Konzept gerecht wird.

    Hast Du irgendwo vergessen zu Deinem Beispiel die Extension "Handles" mit anzugeben oder eine andere Lösung im Sinne des EBC-Konzeptes damit die Lösung läuft? Genau diese Stelle ist ja sehr offensichtlich die sehr wichtige Stelle in dem ganzen Konzept. ;)

    Auf den Rest Deines Posts antworte ich Dir später. ;)

    Gruß

    Rainer
    Ja, das war eher als Pseudocode gemeint. Weil die Handles-Extensions noch sehr unaufgeräumt waren, habichse nicht mitgeliefert. Aber erfreulich, dass auch ohne Funktionieren erkennbar ist, wasse tun sollen - dann stimmt also deren Design.
    Anbei also ein Sample
    Dateien
    • MixInTests.zip

      (23,15 kB, 182 mal heruntergeladen, zuletzt: )

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „ErfinderDesRades“ ()


    Ja, das war eher als Pseudocode gemeint. Weil die Handles-Extensions noch sehr unaufgeräumt waren, habichse nicht mitgeliefert. Aber erfreulich, dass auch ohne Funktionieren erkennbar ist, wasse tun sollen - dann stimmt also deren Design.
    Anbei also ein Sample


    Erstmal danke ... hatte ich mir doch gedacht das da eine Extension fehlt. ^^

    Wobei das ja noch nicht das ursprüngliche Konzept sein kann, da fehlt ja das Event-Handling. Wobei ... habe ich das richtig gelesen das der Vordenker dieser EBC-Modelle mittlerweilen auf Events verzichtet und direkt verbindet so wie in Deinem Sample?


    Statt des Vertrages hast du bei EBC einen Delegaten, mit einer klar definierten Signatur.
    Das ist dann kein Vertrag, sondern eine Steckbuchse: Was da reinpasst, passt da rein, und anneres eben nicht. Gewissermaßen die physikalische Form von Stecker und Buchse machen einen zusätzlichen Verbindungsvertrag üflüssig.


    Genau da liegt irgendwo mein Problem, durch den fehlenden Vertrag gibt es keinerlei Compiler-Unterstützung die mir jetzt mitteilt ob ich richtig angesprochen habe.

    Im Prinzip basiert dieses Prinzip ja darauf das jedes Objekt "nur" Delegaten zur Verfügung stellt und der Programmierer muss dann bei der Nutzung der Objekte erstmal die Verbindung Delegate zu Delegate herstelltn, dabei hat er nicht anderes als Hilfe zur Vermeidung von Fehlern beim Anschliessen als die Signatur. Gibt es in einem Objekt mehrere Delegaten mit der gleichen Signatur hat er schon das Problem das er erstmal nachgucken muss um zu prüfen ob er auch die richtigen Delegaten verbindet.

    Das mag in einem Projekt mit 10 Objekten noch irgendwo gehen, aber in Projekten mit 1000 und mehr Objekten? Da hat doch keiner mehr nach Monaten eine Ahnung was er wo gemacht hat. Und jetzt schlägt der Nachteil von EBC voll durch ... nämlich die enorm schwere Lesbarkeit. Wird ja selbst so von Befürwortern des EBC genannt ... ohne den Schaltplan zu haben kannst Du ein EBC-Programm nur schwer bis gar nicht lesen.


    Ja, so isses - ist doch in Ordnung. Man kann sogar konfigurieren, ob dann eine Exception fliegen soll, oder ob nichts passieren eben i.O. ist. Ein solcher Delegat


    Und genau hier liegt mein nächstes Problem. Ein Objekt mit einem Schnittstellen-Vertrag ist für mich eine BlackBox, ich muss keine Ahnung haben was in ihm vorgeht und brauch es auch nicht wissen da ich nur gegen die Schnittstelle progge. Bei EBC muss ich aber wissen was in den Objekten vorgeht da ich ja für die Ansprache ein Kupplungs-Objekt erstellen muss.


    Und wie gesagt: ich sehe nicht, was das mit Entkopplung zu tun hat. MixIn scheint mir ein fabelhaft praktisches Konzept zu sein, um neue Objekte zusammenzukomponieren, ohne in deren SourceCode einzugreifen. Hat natürlich logisch die Beschränkung, dasses, wie bei Extensions überhaupt, nur um Public Member gehen kann.

    Aber entkoppeln würde heißen: ich baue einen Motor ins Auto, und der Fahrer kennt den nicht, weiß nichtmal, dasses sowas ühaupt gibt. Er hat nur sein Gaspedal, mit dem er die Vorwärtskraft einstellt.


    Okay, jetzt habe ich verstanden was Du mit "entkoppeln" meinst, ich nenne das für mich Abhängigkeiten vermeiden.

    Da unterschätzt Du das Konzept der Mixin-Klassen aber gewaltig, es geht genauso gut (vllt sogar besser) als mit EBC.

    In dem Thread den ich zu dem thema Mixin-Klassen erstellt habe, hatte picoflop als Ergänzung das Thema ConditionalWeakTables eingeworfen. Genau das ist der Schlüsselpunkt gewesen der es möglich macht Interfaces zu designen die völlig unabhängig von irgendwelchen Methoden und Funktionen der implementierenden Objects sind, weil alles was nötig ist im Interface selber beinhaltet ist. Bin froh das picoflop das eingeworfen hat, hatte zwar schon zuvor davon gehört aber nicht richtig mit beschäftigt und so wäre ich vermutlich nie selber auf die Möglichkeiten die sich daraus ergeben gekommen.

    Das folgende Beispiel ist simple und baut auf einer Kommunikation von Interface zu Interface auf. Das eine Interface wird in der Klasse Vehicles implementiert und das andere in der Klasse Motor und dadurch werden Vehicles mit Motoren verbunden. Alles was für die Abwicklung nötig ist beinhaltet das Interface in sich selber. Zur Motorseite dient die abstrakte Klasse Motor_Base als "Stecker" wo man alle Arten von Motoren "anstecken" kann. Zur Vehicle-Seite hin habe ich in dem Beispiel keine abstrakte Klasse als Stecker genutzt, sondern die direkte Implementation der Schnittstelle IMotorisation.

    Spoiler anzeigen

    VB.NET-Quellcode

    1. #Region "Interface Motorisation (Motor -> Vehicles)"
    2. Public Interface IMotorisation
    3. Enum EnDriveType
    4. Motor = 1
    5. Muskelkraft = 2
    6. End Enum
    7. Enum EnDriveKind
    8. Benzin = 1
    9. Diesel = 2
    10. Pedale = 3
    11. End Enum
    12. Enum EnPowerType
    13. PS = 1
    14. KW = 2
    15. End Enum
    16. End Interface
    17. Public Interface IMotorisation_Functions
    18. Property Vehicle() As IMotorisation
    19. ReadOnly Property DriveInformation() As String
    20. End Interface
    21. Module mdl_IMotorisation_Extensions
    22. Private NotInheritable Class MotorValues
    23. Friend v_VehicleName As String
    24. Friend v_DriveKind As IMotorisation.EnDriveKind = 0
    25. Friend v_DriveStrength As Double = 0
    26. Friend v_DriveName As String = ""
    27. Friend v_PowerType As IMotorisation.EnPowerType = 0
    28. End Class
    29. Private WeakTable As New ConditionalWeakTable(Of IMotorisation, MotorValues)
    30. <Extension()> _
    31. Public Sub SetMotorValues(ByVal interF As IMotorisation, _
    32. ByVal valVehicleName As String, _
    33. ByVal valDriveKind As IMotorisation.EnDriveKind, _
    34. ByVal valDriveStrength As Double, _
    35. ByVal valDriveName As String, _
    36. ByVal valPowerType As IMotorisation.EnPowerType)
    37. WeakTable.GetOrCreateValue(interF).v_VehicleName = valVehicleName
    38. WeakTable.GetOrCreateValue(interF).v_DriveKind = valDriveKind
    39. WeakTable.GetOrCreateValue(interF).v_DriveStrength = valDriveStrength
    40. WeakTable.GetOrCreateValue(interF).v_DriveName = valDriveName
    41. WeakTable.GetOrCreateValue(interF).v_PowerType = valPowerType
    42. End Sub
    43. <Extension()> _
    44. Public Function VehicleName(ByVal interF As IMotorisation) As String
    45. Return WeakTable.GetOrCreateValue(interF).v_VehicleName
    46. End Function
    47. <Extension()> _
    48. Public Function PowerType(ByVal interF As IMotorisation) As IMotorisation.EnPowerType
    49. Return WeakTable.GetOrCreateValue(interF).v_PowerType
    50. End Function
    51. <Extension()> _
    52. Public Function DriveName(ByVal interF As IMotorisation) As String
    53. Return WeakTable.GetOrCreateValue(interF).v_DriveName
    54. End Function
    55. <Extension()> _
    56. Public Function DriveStrength(ByVal interF As IMotorisation) As Double
    57. Return WeakTable.GetOrCreateValue(interF).v_DriveStrength
    58. End Function
    59. <Extension()> _
    60. Public Function DriveKind(ByVal interF As IMotorisation) As IMotorisation.EnDriveKind
    61. Return WeakTable.GetOrCreateValue(interF).v_DriveKind
    62. End Function
    63. <Extension()> _
    64. Public Function DriveType(ByVal interF As IMotorisation) As IMotorisation.EnDriveType
    65. Select Case interF.DriveKind
    66. Case IMotorisation.EnDriveKind.Benzin, IMotorisation.EnDriveKind.Diesel
    67. Return IMotorisation.EnDriveType.Motor
    68. Case IMotorisation.EnDriveKind.Pedale
    69. Return IMotorisation.EnDriveType.Muskelkraft
    70. End Select
    71. End Function
    72. <Extension()> _
    73. Public Function Movement(ByVal interF As IMotorisation) As String
    74. Dim MovementClass As IMotorisation_Functions = Nothing
    75. Select Case interF.DriveType
    76. Case IMotorisation.EnDriveType.Motor
    77. MovementClass = New Motor_With With {.Vehicle = interF}
    78. Case IMotorisation.EnDriveType.Muskelkraft
    79. MovementClass = New Motor_Without With {.Vehicle = interF}
    80. End Select
    81. Return MovementClass.DriveInformation
    82. End Function
    83. End Module
    84. #End Region
    85. #Region "Motors"
    86. Public MustInherit Class Motor_Base
    87. Implements IMotorisation_Functions
    88. Private m_Vehicle As IMotorisation
    89. Public Property Vehicle As IMotorisation Implements IMotorisation_Functions.Vehicle
    90. Get
    91. Return m_Vehicle
    92. End Get
    93. Set(value As IMotorisation)
    94. m_Vehicle = value
    95. End Set
    96. End Property
    97. Public MustOverride ReadOnly Property DriveInformation As String Implements IMotorisation_Functions.DriveInformation
    98. End Class
    99. Public Class Motor_With
    100. Inherits Motor_Base
    101. Public Overrides ReadOnly Property DriveInformation As String
    102. Get
    103. Return "Ich bin ein " & Vehicle.VehicleName & " " & _
    104. "und werde angetrieben von einem " & [Enum].GetName(GetType(IMotorisation.EnDriveKind), Vehicle.DriveKind) & _
    105. " - Motor mit " & Vehicle.DriveName & _
    106. " und " & Vehicle.DriveStrength & " " & _
    107. [Enum].GetName(GetType(IMotorisation.EnPowerType), Vehicle.PowerType) & " Leistung"
    108. End Get
    109. End Property
    110. End Class
    111. Public Class Motor_Without
    112. Inherits Motor_Base
    113. Public Overrides ReadOnly Property DriveInformation As String
    114. Get
    115. Return "Ich bin ein " & Vehicle.VehicleName & " " & _
    116. "und werde angetrieben über " & _
    117. [Enum].GetName(GetType(IMotorisation.EnDriveKind), Vehicle.DriveKind) & _
    118. " von " & Vehicle.DriveName
    119. End Get
    120. End Property
    121. End Class
    122. #End Region
    123. #Region "Vehicles"
    124. Public Class Vehicles
    125. Implements IMotorisation
    126. End Class
    127. #End Region
    128. Module TestingVehicles
    129. Public Sub MainTest()
    130. Dim Auto As New Vehicles
    131. Dim Fahrrad As New Vehicles
    132. With Auto
    133. .SetMotorValues("Mazda M6", IMotorisation.EnDriveKind.Benzin, 128, "4-Zylinder, 2.0 Liter", IMotorisation.EnPowerType.PS)
    134. MsgBox(.Movement)
    135. End With
    136. With Fahrrad
    137. .SetMotorValues("Mountainbike", IMotorisation.EnDriveKind.Pedale, 0, "Rainer", 0)
    138. MsgBox(.Movement)
    139. End With
    140. MsgBox("Ich bin ein: " & Auto.VehicleName)
    141. MsgBox("Ich bin ein: " & Fahrrad.VehicleName)
    142. End Sub
    143. End Module


    Achte hierbei auf die Klasse Vehicles ... da gibt es zwar eine Schnittstellen-Implementierung aber sonst nichts was man bei der Erstellung der Klasse berücksichtigen müsste. Also völlig unabhängige Klassenentwicklung ohne Rücksicht auf den Motor möglich.

    Wie Du siehst erreichst Du hier genau das was Du als Vorteil von EBC ansiehst: Klasse Vehicle hat keinerlei Ahnung von Klasse Motor und Klasse Motor hat keinerlei Ahnung von der Klasse Vehicle. Als Kupplungs-Objekt dient die Schnittstelle IMotorisation die in Klasse Vehicle implementiert wird und gegen die Schnittstelle IMotorisation_Functions geproggt ist die wiederum die verschiedenen Arten an möglichen Motorisierungen zur Verfügung stellt.

    Gegen den "Stecker" der abstrakten Klasse (hier nur für Motoren) Motor_Base kannst Du nun jede beliebige Motorisierungen proggen und zwar völlig unabhängig davon welches Vehicle dahinter steckt. Man könnte das Beispiel noch weiter ausbauen und die Klasse Vehicle auch durch die abstrakte Klasse Vehicle_Base ersetzen und dann kannst Du jedes x-beliebige Vehicle dagegen proggen ohne das diese Klasse was von dem Motor wissen muss. Die Klasse muss noch nichtmal wissen was für einen Motor sie nutzt das wird ja über die Schnittstelle geregelt.

    Du musst nur im Interface die "Kupplungs-Stelle" vorgeben die die Vehicles mit den passenden Motoren verbindet. Eben genauso wie Du bei EBC ein Kupplungsobjekt erstellen musst.

    Also Entkoppelung erreichst Du hier genauso wie mit EBC ... der einzige Unterschied ist das bei EBC ein drittes Objekt dazu kommt was verbindet und das jedesmal auf's Neue erstellt werden muss, während bei der Schnittstellen-Variante nur was geändet werden muss wenn ein neuer Vehicle-Typ/Motor-Typ dazu kommt.


    Wie da jetzt die MixIn-Idee was einbringt - wie gesagt: k.A. Die Motorleistung erhöhen ist IMO weniger was zum Auslagern ausserhalb des Motors, sondern grade etwas, was als Private gekapselt gehört - all diese dreckigen Ventile, Motoröl, Hitze, Ruß und Gestank - PfuiDeibel!


    Motor und Vehicle müssen immer unterschiedliche Objekte sein, nur so kann die Realität am korrektesten abgebildet werden. Auto und Motor werden ja auch separat und unabhängig voneinander entwickelt und erst im Produktionsprozes zusammen geführt. Aber auch nachträglich kannst Du ja z.B. in einem bestehenden Auto den Motor austauschen und diese theoretische Möglichkeit muss ja eine Software abbilden.

    Aber gut sicherlich Ansichtssache. ;)

    EBC ist mit Sicherheit ein interessanter Ansatz, aber bringt für mich persönlich zuviele Nachteile mit sich:

    - extrem schwere Lesbarkeit und damit erschwerte Wartbarkeit, ohne den EBC-Schaltplan ist so ein Code quasi unlesbar und bei einem Schaltplan mit 1000 Objekten und mehr haste mit Sicherheit auch richtig viel Fun bis Du da passenden Verbindungen gefunden hast

    - erhöhte Fehlergefahr da die Verbindung von zwei Delegaten keiner Kontrolle unterliegt und damit es durchaus passieren kann das man die falschen Delegaten miteinander verbindet, z.B. will man die PS-Leistung des Motors abrufen hat aber in der Eile mit dem Delegaten der KW (gleiche Signatur) zurück wirft verbunden ... bis das auffällt können Ewigkeiten vergehen und dann viel Spaß beim Suchen wo man falsch verbunden hat

    - Code-Unsicherheit bei der Verwendung, wenn mehrere an einem Projekt proggen wird es vermutlich richtig funny wenn da jeder seine eigene Methode der Verbindung von Delegaten mit sich bringt

    Einen Großteil dieser Nachteile kann man vermeiden in dem man die Verbindung der Delegaten über Schnittstellen regelt (wenn ich das richtig verstanden habe, hat das auch der Vordenker dieser EBC-Modelle genauso angedacht). Aber was hat man dann gewonnen? Meiner Meinung nach dann gar nichts mehr, da kann man gleich beim Konzept der Mixin-Klassen bleiben und erstellt Klassen/Logiken die in jedem Projekt anwendbar sind.

    Den einzigen riesen Vorteil den ich sehe bei EBC ist, dass EBC von Haus auf mit ansynchronen Prozessen von der Natur her (wenn man wirklich komplett auf Event-Handling setzt) automatisch funktioniert. Um das mal auf unser Beispiel umzumünzen: Motor wird gestartet und wirft erst True zurück wenn er wirklich läuft damit das Programm die Fahrt beginnen kann oder bei False eben den Fahrer mitteilt das der Motor nicht läuft und er erst gar nicht versuchen braucht los zu fahren.

    Aber gut ... das war eh der nächste Punkt auf meiner Liste: Zu prüfen wie man Event-Handling in das Mixin-Konstrukt sinnvoll einbauen kann. ^^

    Gruß

    Rainer
    Hi!

    Ich wollte eiglich dein Sample nachbauen, aber mir scheint, da stimmt was ganz und gar nicht.
    5 Klassen, 3 Enums, 2 Interfaces - wofür das alles?

    oder guggemal

    VB.NET-Quellcode

    1. <Extension()> _
    2. Public Function DriveType(ByVal interF As IMotorisation) As IMotorisation.EnDriveType
    3. Select Case interF.DriveKind
    4. Case IMotorisation.EnDriveKind.Benzin, IMotorisation.EnDriveKind.Diesel
    5. Return IMotorisation.EnDriveType.Motor
    6. Case IMotorisation.EnDriveKind.Pedale
    7. Return IMotorisation.EnDriveType.Muskelkraft
    8. End Select
    9. End Function
    10. <Extension()> _
    11. Public Function Movement(ByVal interF As IMotorisation) As String
    12. Dim MovementClass As IMotorisation_Functions = Nothing
    13. Select Case interF.DriveType
    14. Case IMotorisation.EnDriveType.Motor
    15. MovementClass = New Motor_With With {.Vehicle = interF}
    16. Case IMotorisation.EnDriveType.Muskelkraft
    17. MovementClass = New Motor_Without With {.Vehicle = interF}
    18. End Select
    19. Return MovementClass.DriveInformation
    20. End Function


    Bei jedem Aufruf wird ein neuer Motor gebaut! Dabei will man doch wohl die DriveInformation eines bestimmten, eingebauten Motors.
    Auch die DriveInformation selbst ist fragwürdig: Das ist im Motor implementiert, macht aber Aussagen auch übers Vehikel? Eigentlich sollte ein Motor nicht wissen, wo er eingebaut ist.
    Und die Logik: ruft man ein Movement ab (vom Vehikel - was aber schlecht erkennbar ist, weil das Interface IMotorisation heißt), dann switcht dieses erstmal den DriveType, und der wird selbst erst über eine weitere Methode mit einem Switch ermittelt, bei dem der DriveKind geswitcht wird. Und dann, wie gesagt, wird ein neuer Motor gebaut, von dem das DriveInfo abgerufen werden kann.

    Das müssteste erst iwie umbauen, dasses für michn Sinn ergibt.


    erreichst Du hier genau das was Du als Vorteil von EBC ansiehst: Klasse Vehicle hat keinerlei Ahnung von Klasse Motor und Klasse Motor hat keinerlei Ahnung von der Klasse Vehicle.
    Tatsächlich ist mir das zu entkoppelt. Also ein Gaspedal sollte im Vehicel noch drinne sein, da denke ich weiterhin OOP: angesteuert wird ein Fahrzeugmotor vom Fahrzeug aus.

    ErfinderDesRades schrieb:


    Ich wollte eiglich dein Sample nachbauen, aber mir scheint, da stimmt was ganz und gar nicht.
    5 Klassen, 3 Enums, 2 Interfaces - wofür das alles?


    Das ist ja auch nur eine Art Test und nicht wirklich praxisfähiger Code. Letztendlich sollte simuliert werden wie man mit der Programmierung Klasse -> Interface = Interface <- Klasse an beiden Ende eine abstrakte Klasse als eine Art Stecker bekommt auf die man x-beliebig viele und auch unterschiedliche Klassen (nur die Basis muss gleich sein ... eh klar) drauf proggen kann.

    Z.B. das Thema von GambaJo könnte man auch auf sein Thema umsetzen an der einen Seite eine abstrakte Klasse für Verbindungen und auf der anderen Seite die abstrakte Klasse für die Nutzung der Verbindung.


    Bei jedem Aufruf wird ein neuer Motor gebaut! Dabei will man doch wohl die DriveInformation eines bestimmten, eingebauten Motors.
    Auch die DriveInformation selbst ist fragwürdig: Das ist im Motor implementiert, macht aber Aussagen auch übers Vehikel? Eigentlich sollte ein Motor nicht wissen, wo er eingebaut ist.


    Du verrennst Dich da gerade in die Details dieses Beispieles. Das Sample ist genauso wenig sinnvoll/praxistauglich aufgebaut wie Dein Sample aus Post 17. Ist ja auch für eine Demo die nur die prinzipielle Funktionsweise aufzeigen soll nicht nötig. ;)


    Das müssteste erst iwie umbauen, dasses für michn Sinn ergibt.


    Das Beispiel soll auch erstmal keinen praxistauglichen Sinn ergeben, sondern nur die Möglichkeiten und Ansätze die Möglichkeiten zu nutzen darstellen.

    Ich schlag mich jetzt doch nicht mit dem Aufbau einer praxistauglichen Software-Architektur für eine Fahrzeug-Simulation rum, nur um eine Demo für die Funktionsweise zu erstellen. *g*


    Auch die DriveInformation selbst ist fragwürdig: Das ist im Motor implementiert, macht aber Aussagen auch übers Vehikel? Eigentlich sollte ein Motor nicht wissen, wo er eingebaut ist.


    Natürlich habe ich es mir hier etwas einfacher gemacht um dem Motor gleich die Reference zum Vehicle mitgegeben. Es macht auch letztendlich Sinn, denn so bestünde die Möglichkeit Events die die Klasse Auto feuert direkt abzuhorchen und darauf zu reagieren ... Tritt auf das Gaspedal = Leistungserhöhung des Motors etc.. Natürlich müsste dafür umgebaut werden das nicht jedes Mal ein neuer Motor implementiert wird ... das ist aber in dem Konzept kein Thema, sondern nur eine minimale Änderung. Man kann z.B. direkt in der Klasse die im WeakTable referenziert wird die Reference-Holder einbauen und damit eine dauerhafte Verbindung zwischen Vehicle und Motor erstellen. Dann würden die Ereignisse in der Klasse Vehicle aber auch über diese Klasse abgehandelt werden und bekommen dort eine direkte Verbindung zu entsprechenden Funktionen der Klasse Motor.

    Das ist das woran ich gerade rumspiele in dem Beispiel-Code. Wenn ich den habe kann ich Dir den gerne mal schicken.

    Tatsächlich ist mir das zu entkoppelt. Also ein Gaspedal sollte im Vehicel noch drinne sein, da denke ich weiterhin OOP: angesteuert wird ein Fahrzeugmotor vom Fahrzeug aus.


    Was jetzt Entkoppelung oder Nicht Entkoppelung? ^^

    Aber Du kannst ja problemlos eine abstrakte Klasse für Vehicles erstellen die über das Interface Motorisierung gezwungen wird ein Funktion Gaspedal zu implementieren, bzw. die MasterClass die Funktion Gaspedal direkt zur Verfügung stellt. Aber dann wirst Du sehen wieso ich es in dem Beispiel nicht umgesetzt habe ... dann würde es nicht mehr für das Vehicle Fahrrad passen. ^^

    Klar, daher im Praxis-Sinne untauglich mein Beispiel. Aber wie gesagt, ging ja nur um eine Demo für die Möglichkeiten. Daraus sinnvolle Konstrukte für die Praxis zu erstellen bleibt jedem selber überlassen. Aber im Sinne von fortgeschrittenen Entwurfs-Pattern macht das Konstrukt Masterclass -> Interface = Interface <- Masterclass schon gut Sinn. Das proggt man einmal und kann das als Vorlage in jedes x-beliebige Projekt einbauen und proggt dann auf die abstrakten Masterclasses an beiden Ende die projektspezifischen Objekte.

    Irgendwann mit viel Zeit werde ich sowas mit Sicherheit mal für das Thema Multi-Threading erstellen denn wie ich mittlerweilen rausgefunden habe eignen sich die ConditionalWeakTables (bzw. die zugewiesene Klasse) hervorragend um innerhalb des Konstruktes Event-Handling zu betreiben.

    Gruß

    Rainer