Autor Beitrag
OldCat
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 77

Win 10 Pro x64
C# (VS 2019), (VS 2022)
BeitragVerfasst: So 12.12.21 14:57 
Bevor ich zu euch ins Forum kam, meinte ich genau zu wissen, dass Methoden in anderen Methoden direkt aufgerufen werden. Das ist es, was ich bis dahin gelernt hatte.

Doch jetzt dämmert es mir, dass das keine gute Idee ist. Ihr habt mir beigebracht: Rufe ich in einer Methode (z.b: Aufgabe: 'Eingabe') eine andere auf (Aufgabe: 'Verarbeitung'), ist das Single Responsibility Principle nicht eingehalten.

Meine Frage ist banal, und stellt sich mir erst jetzt, und nicht vor einem Monat:

Sollten Methoden niemals direkt über eine andere Methode aufgerufen werden? Vor allem, wenn diese vielleicht noch in einer anderen Klasse stehen?

Sollte dem so sein, dann wird mir der Quellcode von Th69 als Vorlage dienen, wie man es richtig macht. Denn er ruft nicht eine Methode direkt über eine andere Methode auf.
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4791
Erhaltene Danke: 1059

Win10
C#, C++ (VS 2017/19/22)
BeitragVerfasst: So 12.12.21 15:32 
So per se kann man das nicht sagen. Es ist halt eine Frage des Inhalts der Methode. Eine Methode sollte nicht zu viel machen, aber natürlich kann sie Teilaufgaben an andere Methoden weiter delegieren (und dessen Rückgabewerte nutzen!). Es ist ja gerade der Sinn von Methoden, daß sie mehrfach benutzt werden können.

Bei den von dir erstellten Methoden hast du einfach zu viel in den Methoden gemacht (Eingabe, Berechnung und/oder Ausgabe) und das hat das Single-Responsibility-Prinzip verletzt.

Zwei wichtige Designprinzipien sind das Top-Down- und Bottom-Up-Design.
Man sollte ein Projekt immer hierarchisch gestalten. Auf den höheren Ebenen (z.B. in der Main-Methode bzw. Application-Klasse) sollten nur die Hauptmethoden des Programms aufgerufen werden und auf den tieferen Ebenen dann die konkrete Implementierung (z.B. Algorithmen, Datei- und Datenbankzugriffe, etc.).
Bei den kleineren Beispielprogrammen (wie dem Taschenrechner) ist diese Aufteilung nur bedingt zu sehen, aber je größer die Projekte werden, desto mehr Gedanken muß man sich machen, welche Klassen (und Methoden) man entwickelt und wie diese untereinander in Beziehung stehen (d.h. welche Methoden von welchen anderen Methoden aufgerufen werden).

Für diesen Beitrag haben gedankt: OldCat
Palladin007
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1282
Erhaltene Danke: 182

Windows 11 x64 Pro
C# (Visual Studio Preview)
BeitragVerfasst: So 12.12.21 16:27 
[eigentlich hatte ich hier noch mehr stehen, aber Th69 hat's mir vorweg genommen]

Ganz falsch ist deine Überlegung aber nicht, sie geht nur sehr viel weiter, als Du vielleicht dachtest. Das Folgende schreibe ich nur, um genau das zu zeigen.
Wenn Du dich jetzt schon abgehängt fühlst, dann lass das lieber aus - oder Du kannst es gedanklich auf in ein paar Monate verschieben :D

Angenommen, eine Klasse A ruft irgendwelche Methoden aus Klasse B auf.
Das wären dann Abhängigkeiten von Klasse A zu Klasse B. Änderungen in Klasse B können Fehler in Klasse A verursachen, die Entwickler müssen also immer Klasse A kontrollieren und die Funktionen testen und ggf. sogar anpassen. Und von Klasse A sind wieder andere Klassen abhängig, was eine Kette auslöst, die sehr schnell sehr groß werden kann.
Genau das will man aber nicht. Man kann es zwar nie ganz vermeiden, aber man kann die Folgen vermindern, indem man:
  1. die Richtung der Abhängigkeiten nach der "Schwierigkeit", etwas daran zu ändern, organisiert: Etwas was schwer zu ändern ist, sollte nicht von etwas abhängen, das leichter zu ändern ist.
    Die bekannteste Form von diesem Prinzip ist die (drei) Schichtenarchitektur: UI->Logik->Daten. Die Datenschicht (Datenbank, etc.) ist schwer zu ändern, die UI dagegen leicht (und ändert sich auch oft), weshalb die UI-Schicht immer die Kontrolle behalten soll und alles Andere nur direkt oder indirekt aufruft, niemals umgekehrt. Die Datenschicht sollte also möglichst wenig Abhängigkeiten haben und die dürfen niemals "über" der Datenschicht liegen.
  2. eine Abstraktion (bei C# meist Interfaces) dazwischen stellt und die von außen injiziert (Dependency Injection).
    Wenn Klasse A nicht weiß, wovon sie wirklich abhängig ist, sondern nur, dass es da eine Methode gibt, die sie braucht, kann man Klasse B leicht durch C oder D ersetzen, ohne den Code von Klasse A anzupassen. Das wichtigste ist nur, dass die Implementierung der Abstraktion sich auch so verhält, wie man es anhand der Abstraktion (anhand Namen, Parameter, Rückgabewert, etc.) erwarten würde. Außerdem kann man so leichter automatische Tests schreiben, was eine gewaltige Sicherheit bieten kann.
Das sind dann aber schon die anderen vier Prinzipien von SOLID ;)
Und die sind für jemanden, der die OOP nicht aus dem FF beherrscht, sehr schwer zu verstehen - wenn nicht sogar unmöglich.

Für diesen Beitrag haben gedankt: OldCat