Best Practice überprüfen von Übergabeparametern

TM69

Bekanntes Mitglied
Was wäre die best practice beim überprüfen von Übergabe parameter?
Beispiel (dient nur zu veranschaulichung die Funktion könnte natürlich größer sein) - natürlich unterdem Geschichtspunkt von Solid:
Java:
public String myFunc(MyClass myClass, int value /* Bereich angenommen zwischen 1-100 */) {
    return myClass.getParameter();
}

übergibt man der Funktion null so wird eine NullPointerException geworfen.
Es stellt sich jetzt die frage stelle ich von außen sicher dass niemals ein null übergeben wird, oder würde ich bei "größern" Funktionen eine Überprüfung der übergebenen Parameter machen?
 

httpdigest

Top Contributor
Die Frage wäre hier, was die Methode im Fall des expliziten Prüfens dann tun würde, wenn z.B. null übergeben wird.
Du könntest hier statt einer NullPointerException eben z.B. eine IllegalArgumentException werfen.
Beides sind aber RuntimeExceptions, müssen also nicht deklariert und nicht gefangen werden.
Das heißt, im schlimmsten Fall gewinnt man dadurch gar nichts.
Und für NullPointerExceptions sind seit Java 11? die Exception-Meldungen ja auch schon ziemlich gut.

Anders sieht es bei Konsistenzprüfungen aus, wie z.B., dass ein Wert immer zwischen 10 und 20 sein muss, etc.
 

KonradN

Super-Moderator
Mitarbeiter
Ich bin es gewohnt, in Methoden ein Validierungsblock zu haben. Und in dem wird dann gezielt eine Exception geworfen. Sprich:
if (argument == null) throw new NullPointerException();

Wenn es klare Regeln gibt, dann kann es Sinn machen, diese separat zu führen. Das wäre dann sowas wie:
if(!isValidValue(value)) throw new ......
(Das ist ja meist etwas, das auch an mehreren Stellen benötigt wird!)

Das ist dann etwas, das in der Methode enthalten ist. Und gewisse Exceptions erben gedanklich ja von der EntwicklerIstBloedException.

Was bezüglich der NPE aber interessant ist und wo so viele ja immer bei anderen Sprachen das so positiv erwähnen: Null Sicherheit. Das kann man auch haben. @NonNull / @NotNull und @Nullable ... Dann prüft z.B. Spotbugs auch, dass da nichts, das Null sein kann, übergeben wird. Oder wenn etwas null sein kann, dann musst Du es prüfen sonst wird gemeckert.

Hintergrund dieser Prüfungen ist halt, dass ein Inkonsistenter Zustand erkannt wird. Den will man sofort stoppen. Wer weiß was noch nicht stimmig ist und was ggf. falsch läuft. Und je näher man an der Ursache ist, desto leichter findet man diese.
 

temi

Top Contributor
Es stellt sich jetzt die frage stelle ich von außen sicher dass niemals ein null übergeben wird, oder würde ich bei "größern" Funktionen eine Überprüfung der übergebenen Parameter machen?
Möglicherweise machst du beides.

Sollte die Methode zu einer Klasse gehören, die du auch in anderen Projekten verwenden oder die du für die Allgemeinheit zur Verfügung stellen möchtest und die nur korrekt funktioniert, wenn gewisse Bedingungen stimmen, dann sollte die Klasse diese auch prüfen und mit entsprechenden Exceptions reagieren. Einfach nur, damit es nicht zu einer Ausführung kommt, mit einem ungewolltem/falschem Ergebnis.

Gleichzeitig kann es allerdings auch für das Programm, das die Klasse verwendet, sinnvoll sein, die Wertebereiche vorher zu prüfen. Ein Benutzer soll beispielsweise eine fehlerhafte Eingabe wiederholen können, ohne, dass er Ausnahmen um die Ohren geworfen bekommt. Ja, die Ausnahme könnte man fangen und dann die Eingabe wiederholen lassen, aber i. d. R. sollte man Exceptions nicht zur Programmsteuerung verwenden.
 

KonradN

Super-Moderator
Mitarbeiter
Ich verzichte... Das sollte doch eigentlich auf der Hand liegen
Schade, ich hätte doch auf ein paar Erläuterungen gehofft.

Ich sehe halt wirklich kein Problem. Meinst Du mit Abhängigkeit:
  • Die Einbindung dieser Annotations in das Projekt? Teilweise ist das schon direkt mit drin (z.B. Spring mit org.springframework.lang Namespace) oder man braucht keine Library, weil man die zwei Annotations auch direkt selbst in sein Code packen könnte. Müssen ja nur für die Auswertung da sein.
  • das Tool, dass die statische Codeanalyse macht? Also z.B. Spotbugs? Da sehe ich auch nicht wirklich ein Problem - das sind ja einfache Tool Abhängigkeiten.

Daher liegt es zumindest für mich nicht auf der Hand. Und eine systematische Benutzung dieser Annotations führt dann im Anschluss dazu, dass man eben keine NPEs mehr bekommen kann. Das kann man ja durchaus wollen und als sinnvoll erachten.

Hast du so immer noch die "guten"/neuen NullPointerExceptions?
Das Codebeispiel war schlicht falsch. Es wird natürlich nicht manuell eine NPE geworfen. Es wird eine IllegalArgumentException geworfen. Denn genau das ist das Problem: Das Argument entspricht nicht der dokumentierten Schnittstelle.
 

LimDul

Top Contributor
Ich kann die Non-Null Annotationen + SpotBugs nur empfehlen. Wir nutzen die schon länger in der Firma. Dabie mit Default NonNull in der package-info, was die Lesbarkeit erhöht. Damit müssen nur die Ausnahmen annotiert werden. Und da nutzen wir auch konsequent CheckForNull und nicht Nullable, weil CheckForNull deutlich stärker ist. Es zwingt einen beim Zugriff auf null zu prüfen, während nullable das dem Programmier freistellt.

Ich sehe da zwei riesige Vorteile:
a) Die Wahrscheinlichkeit von NPEs wird deutlich verringert.
b) Es sorgt für eine saubere API. Zig-Null Checks einzubauen um die spotBugs Findings (die korrekt sind!) wegzubekommen ist lästiger und aufwändiger als eine saubere API zu bauen, die sicherstellt, dass ich keine Null-Checks brauche.


Ansonsten nutzen wir von Google Guava die PreConditions (https://guava.dev/releases/19.0/api/docs/com/google/common/base/Preconditions.html) weil das erlaubt es die Checks am Anfang der Methode kürzer zu schreiben, so dass ich kein if-Statement brauche und der Code wird lesbarer. Wer die Abhängigkeit nicht will (allerdings ist oft eh da), kann sich so was auch schnell selber bauen.

Es wird nicht immer alles konsequent genutzt, aber insbesondere bei public API die von anderen Komponenten verwendet wird, hat es sich bewährt.
 

TM69

Bekanntes Mitglied
Ich kann die Non-Null Annotationen + SpotBugs nur empfehlen. Wir nutzen die schon länger in der Firma. Dabie mit Default NonNull in der package-info, was die Lesbarkeit erhöht. Damit müssen nur die Ausnahmen annotiert werden. Und da nutzen wir auch konsequent CheckForNull und nicht Nullable, weil CheckForNull deutlich stärker ist. Es zwingt einen beim Zugriff auf null zu prüfen, während nullable das dem Programmier freistellt.

Ich sehe da zwei riesige Vorteile:
a) Die Wahrscheinlichkeit von NPEs wird deutlich verringert.
b) Es sorgt für eine saubere API. Zig-Null Checks einzubauen um die spotBugs Findings (die korrekt sind!) wegzubekommen ist lästiger und aufwändiger als eine saubere API zu bauen, die sicherstellt, dass ich keine Null-Checks brauche.


Ansonsten nutzen wir von Google Guava die PreConditions (https://guava.dev/releases/19.0/api/docs/com/google/common/base/Preconditions.html) weil das erlaubt es die Checks am Anfang der Methode kürzer zu schreiben, so dass ich kein if-Statement brauche und der Code wird lesbarer. Wer die Abhängigkeit nicht will (allerdings ist oft eh da), kann sich so was auch schnell selber bauen.

Es wird nicht immer alles konsequent genutzt, aber insbesondere bei public API die von anderen Komponenten verwendet wird, hat es sich bewährt.
Verstehe deine Aussage nicht so ganz. Könntest du mir ein Beispiel dafür geben, bitte?

Nachtrag: Ich würde gerne das, wenn eine Exception geworfen wird, auch eine Aufzeichnung in einem Log stattfindet z.B. durch Log4J. Ich habe aber noch nichts gefunden bzgl. zusammenspiel Guava und Log4J.
 

KonradN

Super-Moderator
Mitarbeiter
Verstehe deine Aussage nicht so ganz. Könntest du mir ein Beispiel dafür geben, bitte?

Da wäre meine Empfehlung: Spiel einmal etwas mit SpotBugs herum. In meinem Maven Projekt unter https://github.com/kneitzel/JavaMavenApp ist das schon alles eingebunden.

In Greeting habe ich bereits ein paar Annotations bezüglich NotNull - da kannst Du ja mal ein null Wert zuweisen. Schau einfach mal, was da Spotbugs für Meldungen bringt. (Log findet sich im target Verzeichnis. Die Zeilenumbrüche sind nicht so toll - da einfach mal die XML Datei von IntelliJ formatieren lassen (Oder eben von Deiner IDE - sollte jede IDE können).

Nachtrag: Ich würde gerne das, wenn eine Exception geworfen wird, auch eine Aufzeichnung in einem Log stattfindet z.B. durch Log4J. Ich habe aber noch nichts gefunden bzgl. zusammenspiel Guava und Log4J.
Du kannst überall, wo Du ein throw baust ja auch gerne etwas ins Log schreiben. Das ist zwar (im anderen Thread? Oder täuscht mich meine Erinnerung) als Anti Pattern bezeichnet worden (So ich mich nicht irre), aber das sind harte Worte. In der Regel ist es nicht notwendig - und wenn die Nachricht gleich ist, dann sollte die Exception irgendwo ja auch gefangen werden und damit im Log landen. Daher hast Du eine Information doppelt geschrieben.

Aber Anti Pattern ist ein starkes Wort. Es kann durchaus sein, dass Du beim Werfen der Exception mehr oder andere Informationen schreiben willst, die dann ggf. in einem eigenständigen Log landen können. Das ginge dann ggf. etwas Richtung Tracing oder so.

Was da Sinn macht oder eben nicht kannst nur Du entscheiden. Und technisch spricht da überhaupt nichts gegen.
 

TM69

Bekanntes Mitglied
Nachtrag:
Da wäre meine Empfehlung: Spiel einmal etwas mit SpotBugs herum. In meinem Maven Projekt unter https://github.com/kneitzel/JavaMavenApp ist das schon alles eingebunden.

In Greeting habe ich bereits ein paar Annotations bezüglich NotNull - da kannst Du ja mal ein null Wert zuweisen. Schau einfach mal, was da Spotbugs für Meldungen bringt. (Log findet sich im target Verzeichnis. Die Zeilenumbrüche sind nicht so toll - da einfach mal die XML Datei von IntelliJ formatieren lassen (Oder eben von Deiner IDE - sollte jede IDE können).


Du kannst überall, wo Du ein throw baust ja auch gerne etwas ins Log schreiben. Das ist zwar (im anderen Thread? Oder täuscht mich meine Erinnerung) als Anti Pattern bezeichnet worden (So ich mich nicht irre), aber das sind harte Worte. In der Regel ist es nicht notwendig - und wenn die Nachricht gleich ist, dann sollte die Exception irgendwo ja auch gefangen werden und damit im Log landen. Daher hast Du eine Information doppelt geschrieben.

Aber Anti Pattern ist ein starkes Wort. Es kann durchaus sein, dass Du beim Werfen der Exception mehr oder andere Informationen schreiben willst, die dann ggf. in einem eigenständigen Log landen können. Das ginge dann ggf. etwas Richtung Tracing oder so.

Was da Sinn macht oder eben nicht kannst nur Du entscheiden. Und technisch spricht da überhaupt nichts gegen.
Ich habe noch nichts über Anti-Pattern geschrieben, zumindest nicht das ich wüsste ;)

Um es nochmal zu verdeutlichen, mir geht es darum, dass im Fehlerfall (im Produktiv - Einsatz) eine Exception geworfen wird, diese Informationen zum, wie du schon richtig sagtest tracing - dem späteren Nachverfolgen in einer Datei landen soll, die ich mir ggf. später ansehen kann.
 
Zuletzt bearbeitet:

KonradN

Super-Moderator
Mitarbeiter
Ich habe noch nichts über Anti-Pattern geschrieben, zumindest nicht das ich wüsste ;)
Nicht Du - @Oneixee5 hatte n https://www.java-forum.org/thema/log4j-integrieren-wie.199090/ etwas von Bad Practice geschrieben. Ich hatte das also falsch im Kopf. (Bad Practice bei genau dem Code würde ich auch unterstreichen!)

Um es nochmal zu verdeutlichen, mir geht es darum, dass im Fehlerfall (im Produktiv - Einsatz) eine Exception geworfen wird, diese Informationen zum, wie du schon richtig sagtest tracing - dem späteren Nachverfolgen in einer Datei landen soll, die ich mir ggf. später ansehen kann.
Ja, ich denke, dass dies so durchaus verstanden wurde.

Und das, was Du in dem anderen Thread als mögliche Lösung geschrieben hast, geht technisch:
Java:
            String message = "CountryService::createCountry(" + creator + ", " + country + "): The creator must not be null";
            logger.error(message);
            throw new NotDefinedException(message);
Aber wenn sowas als Bad Practice bezeichnet wird, dann wird uns ja irgendwas aufstoßen.

Wenn Du da so eine Exception wirfst, dann sollte diese irgendwo gefangen werden. Es wird also etwas geben wie:
Java:
catch (NotDefinedException ex) {
    log.error("Something bad happend!", ex);
    // ....
}
(Das String Literal wäre da natürlich mit etwas sinnvollerem Inhalt gefüllt und nicht nur ein "Something bad happend!" :) )
Wichtig ist: Du gibst in dem catch auch die Exception mit. Die landet da also auch im Log. Damit hättest Du eine Meldung gleich zwei Mal in dem Log:
a) Beim werfen der Exception
b) Beim fangen der Exception

Daher ist das so erst einmal nicht ganz so toll.

Was kann also Sinn machen?
Ich sehe hier vor allem unterschiedliche Informationen. D.h. Du hast etwas wie:
Code:
log.error("Details, die auf dem Level der Methode / Klasse wichtig sind.");
log.warn("Noch mehr Details - auf anderen Levels .... bis hin zu tiefen Details auf Trace Ebene");
throw new GanzTolleException("High Level Beschreibung.")

Die Kernidee ist ja, dass Du unterschiedliche Level hast. Wieder typisches Auto Level:
  • Der Motor hat ein Problem. Die ganzen Details kann der Motor dann Loggen / Tracen. Das sind dann aber Low Leven Informationen, wozu man die Internas des Motors kennen muss. Irgendwas ist mit eine Zylinderkopfdichtung oder so.
  • Nach außen gibt es aber dann ein allgemeineres Problem. Das Auto zeigt also nur an: Fehlfunktion des Motors.
Das findet sich dann an unterschiedlichen Stellen. Das "Motor Log" ist nur mit speziellem Gerät auslesbar und so. Das andere findet sich direkt als Warnlampe mit kurzem Text im Amaturenbrett.
(Vergleich hinkt etwas - aber es wird vielleicht deutlich, was gemeint ist.)

Sowas macht aber auch nur dann Sinn, wenn Du sozusagen unterschiedliche Module hast. Die dann unterschiedlich konfiguriert werden bezüglich Logging. Du wirst innerhalb eines Bereiches sowas nicht machen. Da muss man nur einmal etwas loggen da alles ja auch in einer Datei landet.

Konnte diese Erklärung etwas helfen? Oder habe ich Dich evtl. falsch verstanden? Reden wir ggf. aneinander vorbei?
 

TM69

Bekanntes Mitglied
Nicht Du - @Oneixee5 hatte n https://www.java-forum.org/thema/log4j-integrieren-wie.199090/ etwas von Bad Practice geschrieben. Ich hatte das also falsch im Kopf. (Bad Practice bei genau dem Code würde ich auch unterstreichen!)


Ja, ich denke, dass dies so durchaus verstanden wurde.

Und das, was Du in dem anderen Thread als mögliche Lösung geschrieben hast, geht technisch:
Java:
            String message = "CountryService::createCountry(" + creator + ", " + country + "): The creator must not be null";
            logger.error(message);
            throw new NotDefinedException(message);
Aber wenn sowas als Bad Practice bezeichnet wird, dann wird uns ja irgendwas aufstoßen.

Wenn Du da so eine Exception wirfst, dann sollte diese irgendwo gefangen werden. Es wird also etwas geben wie:
Java:
catch (NotDefinedException ex) {
    log.error("Something bad happend!", ex);
    // ....
}
(Das String Literal wäre da natürlich mit etwas sinnvollerem Inhalt gefüllt und nicht nur ein "Something bad happend!" :) )
Wichtig ist: Du gibst in dem catch auch die Exception mit. Die landet da also auch im Log. Damit hättest Du eine Meldung gleich zwei Mal in dem Log:
a) Beim werfen der Exception
b) Beim fangen der Exception

Daher ist das so erst einmal nicht ganz so toll.

Was kann also Sinn machen?
Ich sehe hier vor allem unterschiedliche Informationen. D.h. Du hast etwas wie:
Code:
log.error("Details, die auf dem Level der Methode / Klasse wichtig sind.");
log.warn("Noch mehr Details - auf anderen Levels .... bis hin zu tiefen Details auf Trace Ebene");
throw new GanzTolleException("High Level Beschreibung.")

Die Kernidee ist ja, dass Du unterschiedliche Level hast. Wieder typisches Auto Level:
  • Der Motor hat ein Problem. Die ganzen Details kann der Motor dann Loggen / Tracen. Das sind dann aber Low Leven Informationen, wozu man die Internas des Motors kennen muss. Irgendwas ist mit eine Zylinderkopfdichtung oder so.
  • Nach außen gibt es aber dann ein allgemeineres Problem. Das Auto zeigt also nur an: Fehlfunktion des Motors.
Das findet sich dann an unterschiedlichen Stellen. Das "Motor Log" ist nur mit speziellem Gerät auslesbar und so. Das andere findet sich direkt als Warnlampe mit kurzem Text im Amaturenbrett.
(Vergleich hinkt etwas - aber es wird vielleicht deutlich, was gemeint ist.)

Sowas macht aber auch nur dann Sinn, wenn Du sozusagen unterschiedliche Module hast. Die dann unterschiedlich konfiguriert werden bezüglich Logging. Du wirst innerhalb eines Bereiches sowas nicht machen. Da muss man nur einmal etwas loggen da alles ja auch in einer Datei landet.

Konnte diese Erklärung etwas helfen? Oder habe ich Dich evtl. falsch verstanden? Reden wir ggf. aneinander vorbei?
Ersteinmal bzgl. deinen Anmerkungen und z.B. Client via HTTP-Verbindung aus dem anderen Beitrag wäre ich voll und ganz bei euch. Wenn aber eine "automatischer" Ablauf passiert, z.B. ich habe eine Anwendung auf einem Server laufen, der sagen wir mal immer etwas überprüft (was auch immer dieses sein mag) und unter welchen Umständen auch immer, wird eine Exception geworfen, so muss ich ja beim "Ausfall" wissen, welche Exception mit welcher Meldung geworfen wurde (es muss ja nicht zwangsläufig zu einem Stillstand kommen), die ich mir im Nachhinein ansehen kann. Log - Ausgabe auf Console nützt mir persönlich nicht viel, wenn z.B. diese durch (z.B. neu hochfahren) verschwindet.
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
temi best practice: Parameter überprüfen, wo? Allgemeine Java-Themen 9
S best practice: Einordnung Enitity und Datenklasse Allgemeine Java-Themen 11
Airwolf89 JUnit: Vorschläge/ Best Practice Allgemeine Java-Themen 7
M Best Practice: Daten aufnehmen-speichern-bereitstellen Allgemeine Java-Themen 8
H Best Practice zu vielen konstanten Objekten? Allgemeine Java-Themen 10
F best practice Allgemeine Java-Themen 5
J Input/Output Dateien bearbeiten - "Best Practice" Allgemeine Java-Themen 3
R Statische Klasse: Best practice mit flags (2) Allgemeine Java-Themen 3
musiKk Best Practice für kleine Variationen in gegebenen Modellklassen Allgemeine Java-Themen 11
J Best Practice für implementierung von equals(...) Allgemeine Java-Themen 7
Daniel_L Best Practice zum Löschen von Log-Files? Allgemeine Java-Themen 8
Ameise03 Best&Worst Case bei Insertionsort Allgemeine Java-Themen 10
S Best Practices CopyConstrutor mit ArrayList Allgemeine Java-Themen 1
F Error Logging - best practices? Allgemeine Java-Themen 3
M Best Practices für Undo/Redo Allgemeine Java-Themen 16
G Best Practices Software-Engineering‏ Allgemeine Java-Themen 3
G Best Practices Allgemeine Java-Themen 10
M Best Practices Exception Handling für eigene library Allgemeine Java-Themen 8
S best practise Allgemeine Java-Themen 6
S Array: Anzahl Elemente mit best. Wert zählen Allgemeine Java-Themen 4
M Best Match / Best Fit auf Strings Allgemeine Java-Themen 9
L Java überprüfen lassen, ob sich ein gegebener Pfad / das Programm an sich auf einer CD oder Festplatte befindet Allgemeine Java-Themen 14
killig HashMap mit if-Anweisung überprüfen Allgemeine Java-Themen 4
A String auf Zahlen überprüfen Allgemeine Java-Themen 5
X Bedingung zwei mal überprüfen Allgemeine Java-Themen 4
E Variablen Aus .txt ausgelesener string mit if() überprüfen? Allgemeine Java-Themen 2
H Wie kann ich überprüfen ob das Feld noch frei ist? Allgemeine Java-Themen 5
C Email Versand überprüfen Allgemeine Java-Themen 1
F Vier gewinnt Diagonal überprüfen Allgemeine Java-Themen 2
H Datentypen Typ eines Arrays überprüfen Allgemeine Java-Themen 9
B Datei überprüfen, ob diese Freigeben ist oder fertig geschrieben wurde Allgemeine Java-Themen 3
T String auf Inhalt bzw. Schema überprüfen Allgemeine Java-Themen 12
D Object nach Vererbung mit Class Object überprüfen Allgemeine Java-Themen 4
T Hash von *.class-Datein überprüfen Allgemeine Java-Themen 1
A List<String> auf doppelte Einträge überprüfen Allgemeine Java-Themen 4
S Ziffern auf Existenz überprüfen Allgemeine Java-Themen 13
K Threads Thread überprüfen Allgemeine Java-Themen 3
R ThreadPool - vorhandene thread liste überprüfen bzw. aufräumen Allgemeine Java-Themen 3
H Doublewert auf korrektheit überprüfen Allgemeine Java-Themen 18
C String überprüfen Allgemeine Java-Themen 2
T Methodenparameter zur Compile-Zeit überprüfen Allgemeine Java-Themen 8
K Vollständiges Schreiben überprüfen Allgemeine Java-Themen 4
X Java Schachzüge überprüfen Allgemeine Java-Themen 7
C Array überprüfen Allgemeine Java-Themen 8
J String auf E-Mail überprüfen Allgemeine Java-Themen 4
J DecimalFormat überprüfen Allgemeine Java-Themen 8
G überprüfen ob string unteranderem ein regex enthalten Allgemeine Java-Themen 7
D Kommazahl auf Richtigkeit überprüfen Allgemeine Java-Themen 3
C Reflection Übergabeparamter überprüfen? Allgemeine Java-Themen 3
X XML- Datei überprüfen Allgemeine Java-Themen 2
S String auf ASCII Kodierung überprüfen? Allgemeine Java-Themen 4
T String auf Kommazahl überprüfen Allgemeine Java-Themen 4
P Verbindung überprüfen Allgemeine Java-Themen 5
I Zeile überprüfen Allgemeine Java-Themen 12
J Chars auf Gleichheit mit Klamern/Operatoren überprüfen Allgemeine Java-Themen 3
N Ausgaben (System.out) umlenken und in Unit-Tests überprüfen? Allgemeine Java-Themen 2
J Datum auf Gültigkeit überprüfen Allgemeine Java-Themen 4
P Wie überprüfen ob es sich Hexadezimalzahl handelt? Allgemeine Java-Themen 2
S Klasse einer Methode übergeben und überprüfen Allgemeine Java-Themen 7
M String auf Zeichen überprüfen Allgemeine Java-Themen 5
N String überprüfen ob nur Ziffern enthalten sind!! Allgemeine Java-Themen 8

Ähnliche Java Themen

Neue Themen


Oben