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:
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:
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
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