Gewinde Geschrieben 1. Oktober 2022 Geschrieben 1. Oktober 2022 Hallo zusammen, ich bin gerade dabei einen kleinen Haushaltsrechner zu erstellen (für den privaten Gebrauch). Da ich in diesem Programm einige Techniken verarbeiten möchte, mit denen ich noch nicht so vertraut bin, habe ich mir zum Ziel gesetzt Delegtaen, Linq, SQL u.s.w. in dieses Projekt einfließen zu lassen. Derzeit mache ich mich daran mein Wissen über delegates etwas zu festigen und natürlich auch zu erweitern. Mittels einem Menü soll der Benutzer über ein switch statement diverse Methoden in einer anderen Klasse aufrufen können (man könnte diese auch direkt angeben, allerdings hätte ich gerne einen Delegaten). Bei diesen Methoden handelt es sich um die Verarbeitung der Einzahlung, Auszahlung, Kontoübersicht, Kontowechsel u.s.w. Für diese Auswahlmöglichkeit möchte ich einen Delegaten nutzen. Dabei stoße ich auf ein kleines Problem. Die Methode Kontoübersicht benötigt keine Variable zur Verarbeitung, da diese ja nur auf ein Dictionary mit den einzelnen Transaktionen zugreifen und diese dann natürlich auch gegliedert ausgeben soll. Die Methoden Aus- und Einzahlung dagegen, benötigen allerdings verschiedene Variablen. Einmal eine Beschreibung der Transaktion und zusätzlich natürlich auch den dazugehörigen Wert der Transaktion. Nachdem was ich jetzt über Delegaten weis, müsste ich theoretisch 2 verschiedene Delegaten nutzen. Einen für die Übersicht ohne Parameter und einen für die anderen Methoden mit Parameter. Diese Variante fände ich allerdings recht unschön, da ich gerne einen für alle nutzen würde. Somit kommen wir zu meiner Frage, ob es möglich ist, einen Delegaten so flexibel zu konstruieren um ihm die Möglichkeit zu geben auf die verscheidenen Situationen zu reagieren. Ähnlich wie eine Klasse mit mehreren Konstruktoren z.B. Auch wenn ich denke das meine Frage relativ klar ist, habe ich das Programm als rar Datei mit hochgeladen. Dieses ist allerdings noch ziemlich unfertig und hält sich derzeit auch an keine Konventionen. hierbei handelt es sich wirklich um einen Rohling, welcher später noch komplett überarbreitet werden muss. (Nur für den Fall der Fälle 🙂) Mit freundlichen Grüßen Gewinde Zitieren
Gewinde Geschrieben 1. Oktober 2022 Autor Geschrieben 1. Oktober 2022 Hallo zusammen, also ich habe meine Antwort bekommen, auch hier im Forum. Man sollte sich auch mal mit der asuchfunktion beschäftigen. 😏 konnte es unter Delegaten überladen finden. 👍 Zitieren
Whiz-zarD Geschrieben 1. Oktober 2022 Geschrieben 1. Oktober 2022 vor 10 Stunden schrieb Gewinde: Mittels einem Menü soll der Benutzer über ein switch statement diverse Methoden in einer anderen Klasse aufrufen können (man könnte diese auch direkt angeben, allerdings hätte ich gerne einen Delegaten). Bei diesen Methoden handelt es sich um die Verarbeitung der Einzahlung, Auszahlung, Kontoübersicht, Kontowechsel u.s.w. Keine Ahnung, wie du es umgesetzt hast aber im Grunde ist dies das Command-Pattern. vor 10 Stunden schrieb Gewinde: Für diese Auswahlmöglichkeit möchte ich einen Delegaten nutzen. Dabei stoße ich auf ein kleines Problem. Die Methode Kontoübersicht benötigt keine Variable zur Verarbeitung, da diese ja nur auf ein Dictionary mit den einzelnen Transaktionen zugreifen und diese dann natürlich auch gegliedert ausgeben soll. Schaue dir dazu das Factory-Pattern an. Für das, was du tust, würde ich einen Delegaten nicht empfehlen. Delegaten sind Funktionszeiger, die man aus der prozeduralen Welt kennt. Wenn du dann eh eine Switch-Anweisung verwendest, welche Methode nun aufgerufen werden soll, kannst du auch gleich das Factory-Pattern verwenden. Im Grunde stellt ein Delegate eine Abhängigkeit dar. Daher ließe sich ein Delegate auch klassisch über ein Interface und konkrete Implementierungen lösen, was eher der Objektorientierung entspricht. Ein Delegate ist ja eine Referenz auf eine Methode und Methoden sind in Klassen definiert. Wenn du jetzt eine Klasse hast, in der alle Methoden drinnenstecken, die zum Delegate passen, dann verstößt dies gegen das Single-Responsibility-Principle und Open-Closed-Principle. Also anstatt: public class Foo { public delegate void FooDelegate(int x, int y); public void Do(int x, int y) => this.GetFooDelegate(x, y)?.Invoke(x, y); private FooDelegate GetFooDelegate(int x, int y) => x switch { 0 => this.Do0, 1 => this.Do1, _ => throw new NotImplementedException() }; private void Do0(int x, int y) => Console.WriteLine(x * y); private void Do1(int x, int y) => Console.WriteLine(x / y); } Kann man es so schreiben: public interface IDo { void Do(int x, int y); } public class Foo { private readonly DoFactory _doFactory = new(); public void Do(int x, int y) => _doFactory.Create(x, y).Do(x, y); } public class DoFactory { public IDo Create(int x, int y) => x switch { 0 => new Do0(), 1 => new Do1(), _ => throw new NotImplementedException() }; } public class Do0 : IDo { public void Do(int x, int y) => Console.WriteLine(x * y); } public class Do1 : IDo { public void Do(int x, int y) => Console.WriteLine(x / y); } Sieht im ersten Moment vielleicht nach mehr Code aus aber jetzt kann man alles isoliert von einander testen. Zwar verstößt die Factory immer noch das Open-Closed-Principle aber irgendwelche Tode muss man sterben aber da gibt es auch andere Mittel und Wege. Im oberen Beispiel sind nämlich die einzelnen Implementierungen der Aufgaben privat. D.h. man kann sie nicht einzeln testen. Dies geht nur über einen Umweg über die Do()-Methode, was die Tests für diese Methode komplexer und fehleranfälliger machen, da man implizit auch GetFooDelegate() mittesten muss. Die Klasse hat also gleich mehrere Zuständigkeiten: Sie entscheidet, welche Aufgabe gemacht werden soll und sie implementiert die Aufgaben. Wenn man jetzt überlegt, dass jede Aufgabe noch seine eigenen Klassenmitglieder hat, kann es sehr schnell unübersichtlich werden. Du schreibst ja selber, dass die einzelnen Aufgaben noch andere Informationen benötigen, die dann hier von der Klasse gehalten bzw. ermittelt werden müssen und somit noch mehr Zuständigkeiten hat. Im unteren Beispiel sind sowie die einzelnen Aufgaben als auch die Entscheidung, was ausgeführt werden soll, in separate Klassen eingeteilt, die einzeln getestet werden können. Jede Klasse hat ihre Zuständigkeit und die Klasse Foo dient dann nur noch als Integration. Wenn man z.B. Do() von der Klasse Foo mit den Parametern (1, 1) aufruft, wird eine 1 auf dem Bildschirm ausgewiesen aber es ist im Grunde gar nicht so richtig klar, welche Aufgabe nun ausgeführt wurde, da beide Aufgaben eine 1 ausgeben würden (1 * 1 = 1 und 1 / 1 = 1). Im unteren Beispiel ließe sich aber ein Test für die DoFactory-Klasse schreiben, mit dem wir testen können, dass die richtige Aufgabe gezogen wurde. In beiden Beispielen würde er zwar 1 / 1 rechnen, aber vielleicht wollten wir 1 * 1 rechnen und unsere Factory hätte ein Fehler. Im oberen Beispiel ließe sich der Fehler nicht erkennen. Ich persönlich würde Delegates nur verwenden, wenn ich optionale Aufgaben hätte, die eine Klasse ausführen kann. Beispielsweise Logging- oder Debugging-Ausgaben, die ich von außen steuern und mir den Interface-Overhead sparen möchte. Events sind ja auch Delegates, wenn sie auch etwas speziell sind aber Events sind ja auch was optionales. Delegates werden gerne beim Passive-View MVP-Pattern verwendet, die im Grunde nur eine Weiterleitung der Events der Views darstellt. Ansonsten verzichte ich auf den Einsatz von Delegates. Zitieren
Empfohlene Beiträge
Dein Kommentar
Du kannst jetzt schreiben und Dich später registrieren. Wenn Du ein Konto hast, melde Dich jetzt an, um unter Deinem Benutzernamen zu schreiben.