Modul Entwurfsmuster Prof. Dr. Dominik Gruntz Prof. Dr. Christoph Denzler 6 Merkpunkte für besseren, objektorientierten Code Ungarische Notation. C++ Programmierer sind sich gewohnt, in den Variablennamen den Typ und die Art der Variable (global, statisch oder lokal) einzucodieren. Diese Art der Notation ist von Charles Simonyi von Microsoft in den 70er Jahren eingeführt worden und hilft bei nicht streng typisierten Programmiersprachen wie z.B. C++. Java und C# haben ein strenges Typsystem, und daher empfehlen sowohl die Java Code Conventions [1] als auch die .NET Naming Conventions [2] auf die ungarische Notation zu verzichten. Verwenden Sie daher keine m_ oder _ Präfixe um Instanzvariablen oder lokale Variablen zu markieren. Wenn es hilft, dann verwenden Sie beim Zugriff auf Instanzvariablen das Schlüsselwort this. Ihre Methoden sollten so kurz sein, dass sie als ganzes überblickt werden können und dass einfach festgestellt werden kann, ob eine Variable lokal ist oder nicht. Bei Konstruktoren, in welchen die Werte von Feldern initialisiert werden, empfehlen wir, als Parameternamen die Feldnamen zu verwenden und mit this auf die Instanzvariablen zuzugreifen. public Point(int x, int y) { this.x = x; this.y = y; } Setter- und Getter-Methoden. Zugriff auf Felder sollte immer über Getter- und Setter-Methoden erfolgen. Der Grund ist, dass solche Klassen einfacher durch ein Proxy ersetzt werden können. Wir werden dies im Rahmen des Decorator-Patterns diskutieren. Das Programm wird durch die Verwendung dieser Methoden nicht langsamer ausgeführt, da die JVM diese Methoden eliminieren kann (Inlining). Der Zugriff auf interne Datenstrukturen (sowohl über Setter- als auch über Getter-Methoden) birgt jedoch die Gefahr, dass so unkontrollierter Zugriff auf den Zustand der Objekte ermöglicht wird. Diese Probleme werden wir im Kontext des Prototype-Patterns diskutieren. Program to an Interface, not an Implementation. Bei der Deklaration von Variablen und Methodenparametern soll als Typ wenn möglich ein Interface verwendet werden und nicht eine konkrete oder abstrakte Klasse. Damit ist es möglich, solche Methoden auch mit anderen (und insbesondere mit erst später entwickelten) Implementierungen des Interfaces zu verwenden. Deklarieren Sie also z.B. eine Liste als List<?> list = new LinkedList<?>() Wie unterschiedliche Implementierungen erzeugt werden können, ohne dass dazu der Code, der die Instanz erzeugt, geändert werden muss, werden wir im Kontext der Factory-Patterns sehen. Logische Ausdrücke. Logische Ausdrücke können wie Ausdrücke anderer Typen auch in Zuweisungen und return-Anweisungen verwendet werden; der Code wird dadurch viel lesbarer. Die Methode equals in der Klasse Point kann wie folgt implementiert werden: boolean equals(Object p) { return p instanceof Point && ((Point)p).x == x && ((Point)p).y == y; } Dabei wird auch die Eigenschaft ausgenutzt, dass die logischen && und || Operatoren nur bedingt ausgewertet werden. Vermeiden Sie bei solchem Code if-Anweisungen, insbesondere Anweisungen der Form: if( <boolean-expression> ) { // bad style return true; } else { return false; } Lieber etwas weniger Kommentar, dafür lesbaren Code. Wenn Sie Ihre Programme lesbar formulieren, muss nicht in einem Kommentar beschrieben werden, was die Anweisungen für eine Bedeutung haben. Versuchen Sie also so zu programmieren, dass der Code für sich selber steht und nicht in Kommentaren erklärt werden muss. Verwenden Sie von Anfang an einen Style-Checker. Wenn Sie erst mitten in einem Projekt einen Style-Checker verwenden, werden Sie erdrückt von den vielen Warnungen. Eine grössere RefactoringPhase steht dann an. Style-Checker helfen den Code von Anfang an übersichtlich zu schreiben. [1] http://www.oracle.com/technetwork/java/codeconventions-150003.pdf [2] http://msdn.microsoft.com/en-us/library/ms229045.aspx