pdf-Datei - IT-Sicherheitsinfrastrukturen - Friedrich

Werbung
10
3
5
15
8
13
4
24
18
19
Bachelorstudiengang Informatik/IT-Sicherheit
Programmierkonzepte
[ProgKonz]
Autoren:
Prof. Dr. Felix Freiling
Philipp Klein
Hochschule Erlangen-Nürnberg
Modul
Programmierkonzepte
[ProgKonz]
Studienbrief 6: Coding Conventions und Best Practices
Autoren:
Prof. Dr. Felix Freiling
Philipp Klein
1. Auflage
Hochschule Erlangen-Nürnberg
© 2015 Hochschule Erlangen-Nürnberg
Lehrstuhl für Informatik 1
IT-Sicherheitsinfrastrukturen
Martensstr. 3
91058 Erlangen
1. Auflage (3. Dezember 2015)
Das Werk einschließlich seiner Teile ist urheberrechtlich geschützt. Jede Verwendung außerhalb der engen Grenzen des Urheberrechtsgesetzes ist ohne
Zustimmung der Verfasser unzulässig und strafbar. Das gilt insbesondere
für Vervielfältigungen, Übersetzungen, Mikroverfilmungen und die Einspeicherung und Verarbeitung in elektronischen Systemen.
Um die Lesbarkeit zu vereinfachen, wird auf die zusätzliche Formulierung
der weiblichen Form bei Personenbezeichnungen verzichtet. Wir weisen deshalb darauf hin, dass die Verwendung der männlichen Form explizit als
geschlechtsunabhängig verstanden werden soll.
Das diesem Bericht zugrundeliegende Vorhaben wurde mit Mitteln des Bundesministeriums für Bildung, und Forschung unter dem Förderkennzeichen
160H11068 gefördert. Die Verantwortung für den Inhalt dieser Veröffentlichung liegt beim Autor.
Inhaltsverzeichnis
Seite 3
Inhaltsverzeichnis
Einleitung zu den Studienbriefen
I.
Abkürzungen der Randsymbole und Farbkodierungen . . . . . . . . .
II.
Zu den Autoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
III.
Modullehrziele . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Studienbrief 6 Coding Conventions und Best Practices
6.1 Lernergebnisse . . . . . . . . . . . . . . . . . . . . . . . .
6.2 Advance Organzier . . . . . . . . . . . . . . . . . . . . . .
6.3 Coding Conventions . . . . . . . . . . . . . . . . . . . . .
6.3.1 Namenskonventionen . . . . . . . . . . . . . . . .
6.3.2 Klammersetzung . . . . . . . . . . . . . . . . . . .
6.3.3 Einrückung . . . . . . . . . . . . . . . . . . . . . .
6.3.4 Zeilenlänge . . . . . . . . . . . . . . . . . . . . . .
6.3.5 Zeilenumbrüche . . . . . . . . . . . . . . . . . . .
6.3.6 Encoding . . . . . . . . . . . . . . . . . . . . . . .
6.3.7 Sprache . . . . . . . . . . . . . . . . . . . . . . .
6.4 Dokumentation . . . . . . . . . . . . . . . . . . . . . . . .
6.4.1 Kommentare . . . . . . . . . . . . . . . . . . . . .
6.5 Best Practices . . . . . . . . . . . . . . . . . . . . . . . . .
6.5.1 Testing . . . . . . . . . . . . . . . . . . . . . . . .
6.5.2 Refactoring . . . . . . . . . . . . . . . . . . . . . .
6.5.3 Don’t repeat yourself (DRY) . . . . . . . . . . . . .
6.5.4 Methods should do one thing (nach Martin [2008]
6.5.5 Magic Numbers . . . . . . . . . . . . . . . . . . .
6.5.6 Strings bauen mit StringBuilder . . . . . . . . . . .
6.5.7 Wenn es sicher sein soll: SecureRandom . . . . . .
6.5.8 Eindeutigkeit . . . . . . . . . . . . . . . . . . . . .
6.5.9 Das Rad nicht neu erfinden . . . . . . . . . . . . .
6.5.10 Usereingaben validieren . . . . . . . . . . . . . .
6.6 Zusammenfassung . . . . . . . . . . . . . . . . . . . . . .
6.7 Übungen . . . . . . . . . . . . . . . . . . . . . . . . . . .
4
5
6
11
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
Liste der Lösungen zu den Kontrollaufgaben
Verzeichnisse
I.
Beispiele . . . .
II.
Exkurse . . . . .
III.
Kontrollaufgaben
IV.
Tabellen . . . . .
V.
Literatur . . . . .
4
11
11
11
12
17
17
18
19
20
21
22
22
27
28
28
29
30
32
33
34
35
36
37
40
41
47
49
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
49
49
49
49
49
Liste der Lösungen zu den Übungen
51
Anhang
57
Seite 4
Einleitung zu den Studienbriefen
Einleitung zu den Studienbriefen
I. Abkürzungen der Randsymbole und Farbkodierungen
Beispiel
B
Exkurs
E
Kontrollaufgabe
K
Merksatz
M
Quelltext
Q
Übung
Ü
Zu den Autoren
Seite 5
II. Zu den Autoren
Felix Freiling ist seit Dezember 2010 Inhaber des Lehrstuhls für ITSicherheitsinfrastrukturen an der Friedrich-Alexander-Universität ErlangenNürnberg. Zuvor war er bereits als Professor für Informatik an der RWTH Aachen
(2003-2005) und der Universität Mannheim (2005-2010) tätig. Schwerpunkte
seiner Arbeitsgruppe in Forschung und Lehre sind offensive Methoden der
IT-Sicherheit, technische Aspekte der Cyberkriminalität sowie digitale Forensik.
In den Verfahren zur Online-Durchsuchung und zur Vorratsdatenspeicherung
vor dem Bundesverfassungsgericht diente Felix Freiling als sachverständige
Auskunftsperson.
Philipp Klein ist seit November 2013 wissenschaftlicher Mitarbeiter am Lehrstuhl
für IT-Sicherheitsinfrastrukturen unter der Leitung von Prof. Dr. Felix Freiling.
Im Oktober 2013 erhielt er seinen Abschluss Master of Science in Informatik an
der Friedrich-Alexander-Universität Erlangen-Nürnberg. Während des Masterstudiums legte er seinen Schwerpunkt auf das Thema IT-Sicherheit. Er besitzt
einen Bachelor-Abschluss im Fach Wirtschaftsinformatik der Georg-Simon-OhmHochschule Nürnberg.
Seite 6
Einleitung zu den Studienbriefen
III. Modullehrziele
In dieser Lehrveranstaltung lernen Sie, die Programmiersprache Java zu nutzen, um selbstständig Programme
zu schreiben. Nach Lektüre dieses Moduls sind Sie in der Lage, die meisten Programmieraufgaben, die an
Sie gestellt werden, zu lösen. Sollten Sie ein bestimmtes Konstrukt der Sprache noch nicht gelernt haben, so
können Sie es sich selbstständig in kurzer Zeit aneignen.
Darüber hinaus können Sie sich weitere Programmiersprachen selbstständig aneignen. Die Prinzipien und
Strukturen, die Sie in dieser Lehrveranstaltung erlernen, finden Sie in den meisten anderen Programmiersprachen wieder. Java bietet dabei einen guten Startpunkt, sowohl hardwarenähere Sprachen als auch
beispielsweise Skriptsprachen zu erlernen.
Programmieren ist etwas, das man weniger aus Büchern lernt, sondern anwenden und beherrschen muss.
Aus diesem Grund sind im Studienbrief viele Kontrollaufgaben, die Sie bearbeiten können. Die Lösungen
finden Sie am Ende des Studienbriefs. Darüber hinaus müssen Sie Übungsaufgaben bearbeiten, die Sie am
Ende jedes Studienbriefs finden. Das Bearbeiten dieser Aufgaben ist Pflicht, sodass Sie gezwungen sind,
wirklich zu programmieren. Gleichzeitig bietet das Abgabesystem aber genug Freiheit, um sich die Zeit
selbstständig einzuteilen.
Dieses Modul teilt sich in zwei Lehrveranstaltungen auf. In der ersten Lehrveranstaltung haben Sie die
Grundlagen von Java und des Programmierens erlernt. In der zweiten Lehrveranstaltung werden wir nun
einzelne Aspekte von Java genauer betrachten sowie die Theorie hinter der Programmierung erläutern.
Modullehrziele
Seite 7
Modulbeschreibung
Modulbezeichnung:
Grundlagen der Programmierung
Studiengang:
Bachelor IT-Sicherheit
Verwendbarkeit:
Dieses Modul ist verwendbar für
• Studierende der Informatik
• Studierende der Wirtschaftsinformatik
• Studierende der Mathematik und Informatik
auf Bachelorniveau. Dieses Modul kann nicht als Wahlpflichtmodul gewählt werden, sondern ist ein Pflichtmodul.
Lehrveranstaltungen und
Lehrformen:
Einführung in das Programmieren
Programmierkonzepte
Modulverantwortliche(r):
Prof. Dr. Felix Freiling
Lehrende:
Prof. Dr. Felix Freiling
Dauer:
2 Semester
Credits:
10 ECTS
Studien- und Prüfungsleistungen:
Schriftliche Prüfung: 120 Minuten
Um zur Prüfung zugelassen zu werden, müssen die bereitgestellten Übungsaufgaben in jeder Lehrveranstaltung zu mindestens
70% richtig bearbeitet werden.
Berechnung der Modulnote:
Notwendige Voraussetzungen:
Empfohlene Voraussetzungen:
Keine
Unterrichts- und Prüfungssprache:
Deutsch
Zuordnung des Moduls zu den
Fachgebieten des Curriculums:
Einordnung ins Fachsemester:
Ab Studiensemester 1
Generelle Zielsetzung des Moduls:
Modul zur Förderung und Verstärkung der Fachkompetenz
Seite 8
Arbeitsaufwand bzw.
Gesamtworkload:
Einleitung zu den Studienbriefen
Für dieses Modul:
Präsenzzeit: 60 h
• Vorlesungsteil: 20 h
• Übungsteil: 10 h
• Praktischer Teil: 20 h
• Prüfungsvorbereitungsveranstaltung: 8 h
• Prüfung: 2 h
Eigenstudium: 240 h
• Durcharbeiten der Studienbriefe: 100 h
• Wahrnehmen der Online Betreuung und Beratung: 20 h
• Ausarbeiten von Aufgaben: 100 h
• Individuelle Prüfungsvorbereitung der Studierenden: 20 h
Modullehrziele
Lerninhalt und Niveau:
Seite 9
Einführung in das Programmieren
Eine Einführung in die Programmiersprache Java. Mit Hilfe der
Entwicklungsumgebung BlueJ wird den Studierenden der Umgang mit Java und Objektorientierung vertraut gemacht. Themen
sind unter Anderem:
• Ausdrücke und Algorithmische Kernsprache von Java
• Sprachbeschreibung und Objekttypen
• Eine Einführung in bereits existierende Methoden und Klassen in der Programmiersprache Java
• Testen und Test Driven Development mit JUnit
Darüber hinaus erhalten die Studierenden einen praktischen Einblick in die folgenden programmierrelevanten Technologien/Techniken:
• Versionsverwaltung mit Git
Programmierkonzepte
Diese Lehrveranstaltung knüpft nahtlos an die Veranstaltung „Einführung in das Programmieren“ an. Die Studierenden lernen weitere Komponenten der Programmiersprache Java kennen, wie
beispielsweise:
• Coding Conventions und Best Practices
• Exceptionhandling
• I/O-Verarbeitung
• Modellierung mit UML
• Rekursion
• Programmiertheorie (Verifikation, Asymptotisches Laufzeitverhalten)
• Einbinden von Datenbanken
Neben diesen Inhalten lernen die Studierenden ebenso theoretische Grundlagen der Programmierung kennen, wie die Laufzeitanalyse und die Korrektheit von Algorithmen.
Das Niveau der Lerninhalte liegt gemessen am DQR-Niveau bei 6
(Bachelor)
Seite 10
Angestrebte Lernergebnisse:
Einleitung zu den Studienbriefen
Fachkompetenz: Die Studierenden können beliebige Programme
in Java erstellen. Sprachkomponenten, die Sie noch nicht kennen,
können Sie sich in kürzester Zeit aneignen. Zudem sind die Studierenden in der Lage, sich selbstständig neue Programmiersprachen
beizubringen. Sie schreiben sichere Programme und wissen, wo
potentielle Schwachstellen in einem Programm zu finden sind.
Methodenkompetenz: Die Studierenden beherrschen den Umgang
mit beliebigen IDEs. Sie können fremde Programme untersuchen und den Kontrollfluss nachvollziehen. Sie sind in der Lage,
Schwachstellen und Fehler in einem Programm zu finden und zu
beseitigen.
Sozialkompetenz: Durch das gemeinsame Lösen von Aufgaben erlangen die Studierenden die Fähigkeit, eigene Handlungsziele
mit den Einstellungen und Werten einer Gruppe zu verknüpfen
und ihre Teamfähigkeit zu stärken. In der Präsenzphase erlangen
sie u.A. durch Pair-Programming die Kompetenz, eigene Ideen
gegenüber einem anderen Programmierer zu kommunizieren,
Kompromisse zu bilden und diese im Team umzusetzen.
Selbstkompetenz: Die Studierenden erlangen die Fähigkeit zur Bildung einer Meinung über eigene Programme und Programme
anderer. Darüber hinaus erlangen sie die Fähigkeit, in komplexen
Situationen zu handeln und eine Lösung für komplexe Probleme
zu finden.
Häufigkeit des Angebots:
Wintersemester
Anerkannte Module:
Anerkannte anderweitige Lernergebnisse / Lernleistungen:
Medienformen:
Literatur:
Studienbriefe in schriftlicher und elektronischer Form, Onlinematerial in Lernplattform, Übungen und Projekt über Lernplattform,
Online-Konferenzen, Chat und Forum, Präsenzveranstaltung mit
Rechner und Beamer
• Touch of class, Betrand Mayer, 2009
• IT-Sicherheit, Claudia Eckert, 2012
• Handbuch Java Band 1, Universität Hannover, 2010
• Einführung in Java mit BlueJ, Florian Siebler, 2011
• Java lernen mit BlueJ, David J. Barnes, Michael Kölling. 2006
• Weitere Literatur wird in der Lehrveranstaltung bekannt
gegeben.
Studienbrief 6 Coding Conventions und Best Practices
Studienbrief 6 Coding Conventions und Best Practices
6.1 Lernergebnisse
Nach der Lektüre dieses Studienbriefes sind Sie mit Coding Conventions und einigen
Best Practices in Java vertraut. Sie sind in der Lage, besser lesbaren und strukturierten Code zu schreiben. Darüber hinaus kennen Sie einige Fallstricke, die beim
Programmieren in Java beachtet werden müssen.
Sie wissen, dass es keinen perfekten Code gibt. Am Ende dieses Studienbriefes
sind Sie aber in der Lage, besseren Code zu schreiben als vorher. Hierfür haben
Sie einige Konventionen und Regeln kennengelernt.
Um wieder warm zu werden, finden Sie im Übungsteil dieses Studienbriefes einige
Aufgaben. Die Lösungen dieser Aufgaben sind am Ende des Dokuments zu finden.
Durch das Bearbeiten dieser Aufgaben sind Sie wieder auf dem Wissensstand, um
sich mit den restlichen Studienbriefen zu befassen.
Grundlage für diesen Studienbrief sind die Bücher Clean Code - A Handbook of Agile
Software Craftsmanship von Robert C. Martin (Martin [2008]) sowie das Buch Weniger
Schlecht Programmieren von Katrin Passig und Johannes Jander (Passig [2013]).
Für die Coding Conventions wurde auf den Google Java Style (Google [2014]) und
die Code Conventions for the Java Programming Language von Oracle (Oracle [1999])
zurückgegriffen.
6.2 Advance Organzier
In den bisherigen Studienbriefen lag der Fokus darauf, zu lernen, wie Sie Anforderungen an ein Programm in Java umsetzen. Sie haben die allgemeine Syntax von
Java kennengelernt, mit ihren Einschränkungen und Besonderheiten.
Damit der Compiler Ihr Programm versteht, müssen Sie sich lediglich an die korrekte Syntax halten. Dem Compiler ist es egal, ob Sie Ihren Code dokumentiert haben,
ob Sie aussagekräftige Namen benutzen usw. Sie könnten Ihren Programmcode
in eine einzige Zeile schreiben, mit einbuchstabigen Variablennamen und ohne
mehrere Klassen zu verwenden. Solange die Syntax stimmt, wird der Compiler
ihr Programm übersetzen können.
In diesem Studienbrief sollen Sie lernen, Programme zu schreiben, die von anderen
Programmierern gelesen und genutzt werden können. Ziel dabei soll es sein, leicht
verständlichen und übersichtlichen Code zu produzieren, der sich an gewisse
Regeln hält.
6.3 Coding Conventions
Coding Conventions (auch: Code Conventions, dt.: Programmierstil) sind Regeln und
Richtlinien, die vorgeben, wie programmiert wird. Es wird beispielsweise festgelegt,
welche Konventionen für Namen existieren, wie Zeilenumbrüche erfolgen oder
wie lang eine Zeile sein darf.
Ziel dieser Konventionen ist es, eine einheitliche Struktur in Ihren Code zu bringen.
Dies ist besonders wichtig, wenn mehrere Programmierer an dem selben Projekt
arbeiten.
Seite 11
Seite 12
Studienbrief 6 Coding Conventions und Best Practices
Coding Conventions sollten vor einem Projekt festgelegt werden. Das Team sollte
sich verpflichten, sich an diese zu halten. Sollte dies nicht geschehen, wird jeder
Entwickler seine eigenen Konventionen nutzen – eine einheitliche Struktur des
Codes ist so nicht möglich.
Die Konventionen, die wir Ihnen auf den nächsten Seiten vorstellen, sind individuell für ein Projekt anpassbar. Bei den Namenskonventionen sollten Sie jedoch nicht
abweichen. Was aber Zeilenlänge, Zeilenumbrüche oder Dokumentation angeht:
Hier können für jedes Projekt individuelle Anpassungen vorgenommen werden.
Wichtig ist lediglich, sich vorab auf Coding Conventions zu einigen.
6.3.1 Namenskonventionen
Mit den Namenskonventionen haben wir uns bereits in Studienbrief 2 befasst. Dort
haben wir die üblichen Konventionen vorgestellt, als wir die jeweiligen Programmkonstrukte, wie Variablen oder Methoden, eingeführt haben. In diesem Abschnitt
fassen wir diese Namenskonventionen noch einmal zusammen und ergänzen, wo
nötig, einige Regeln.
Allgemeines Ziel von Namenskonventionen ist es, auf den ersten Blick zu sehen,
um welche Art von Programmierbaustein es sich bei dem vorliegenden Konstrukt
handelt. Stellen Sie sich einmal vor, Sie betrachten fremden Quellcode und wissen
nicht, ob im vorliegenden Programmteil eine Methode oder eine Variable benutzt
wird. Sicherlich lassen sich Methoden daran erkennen, dass sie am Ende runde
Klammern besitzen – aber das sollte nicht das einzige sein, was eine Methode von
einer Variablen unterscheidet.
Neben diesem offensichtlichen Ziel gibt es aber noch ein weitaus wichtigeres.
Durch Namenskonventionen, an die sich alle Entwickler halten, kann der Code
leichter von anderen Personen verstanden werden. Diese benutzen die selben
Konventionen – und fühlen sich so in Ihrem Code nicht gänzlich unwohl.
Klassen
Bei der Wahl eines Klassennamens greift man für gewöhnlich auf Substantive oder
eine Kombination von Substantiven zurück. Dies lässt sich dadurch begründen,
dass eine Klasse eine allgemeine Beschreibung eines Objekts ist. Wenn Sie also
Objekte anlegen bzw. beschreiben möchten, die geometrische Formen abbilden, so
würden Sie als Klassennamen etwa GeometricalForm wählen.
Bei der Benennung von Klassen wird auf den Upper Camel Case zurückgegriffen.
Wenn der Klassennamen aus mehreren Wörtern besteht, werden alle Wörter hintereinander, ohne Leerzeichen, geschrieben. Der erste Buchstabe jedes Wortes wird
groß geschrieben.
Dateinamen
Der Dateiname richtet sich nach dem Namen der Klasse, die sich darin befindet.
Um den Dateinamen zu bilden, wird an den Klassennamen die Dateiendung .java
angehängt.
Wenn sich mehrere Klassen in einer Datei befinden, ist es an der Zeit, diese Klassen
in jeweils eine eigene Datei zu speichern. Eine Ausnahme hiervon bilden innere
Klassen. Auf diese gehen wir gesondert in Studienbrief 11 ein, wo wir uns mit
GUI-Programmierung beschäftigen.
6.3 Coding Conventions
Seite 13
Interfaces
Bei der Bezeichnung von Interfaces greift man auf Substantive oder Adjektive zurück. Im Falle eines Adjektivs enden diese meist auf ~able. Im Deutschen würden
wir solche Adjektive mit dem Suffix ~bar übersetzen.
Beispiel 6.1: Namenskonventionen bei Interfaces
B
Einige Namen von Interfaces, wie sie in der Standard-Java-API enthalten
sind:
Comparable
Iterable
Drawable
List
Map
Serializable
Wie zuvor bei den Klassen, wird auch bei Interfaces der Upper Camel Case benutzt.
Damit sind Klassen und Interfaces auf den ersten Blick nicht immer sofort voneinander unterscheidbar. Man trifft daher häufig auf Interfaces, an denen das Wort
Interface am Ende angehängt wurde.
Beispiel 6.2: Interface als Suffix
Beachten Sie: Nur weil Interface in einem Namen vorkommt, handelt es
sich nicht zwangsläufig um ein Interface. Die Klasse NetworkInterface1
der Android-API beispielsweise ist eine Klasse.
Methoden
In einem Methodennamen sollte immer ein Verb vorkommen. Schließlich sind
Methoden Dinge, die ein Objekt tun kann oder die wir mit einem Objekt tun
können.
Bei der Wahl des Namens sollte darauf geachtet werden, dass der Klassenname bei
der Benennung berücksichtigt wird. Wenn Sie den Namen der Methode festlegen,
denken Sie sich den Klassennamen vor dem Methodennamen. Zum Verständnis
ein Beispiel:
class Car
{
public void driveCar()
// code
}
Das obere Beispiel ist ein schlechtes. Wenn Sie jemand fragen würde, was man mit
einem Auto tun kann, würden Sie wohl kaum mit „autofahren“ antworten. Die
natürlichere Antwort wäre: „fahren“.
1 http://developer.android.com/reference/java/net/NetworkInterface.html
B
Seite 14
Studienbrief 6 Coding Conventions und Best Practices
Bei der Benennung einer Methode sollten Sie dieses Prinzip ebenfalls berücksichtigen. Der Klassenname gehört zum Methodennamen dazu. Wenn Sie von der
Methode sprechen, würden Sie sagen: „Die Methode drive der Klasse Car“.
Damit sollte auch klar sein, wie wir den oben stehenden Code verbessern können:
class Car
{
public void drive()
// code
}
Wie Sie dem ersten, schlechten Beispiel wahrscheinlich schon entnommen haben,
werden Methodennamen, falls sie aus mehreren Wörtern bestehen, in lower Camel
Case geschrieben. Hier wird der erste Buchstabe des ersten Wortes klein geschrieben.
Der jeweils erste Buchstabe aller folgenden Wörter wird groß geschrieben.
B
Beispiel 6.3: Namenskonventionen bei Methoden
Einige Beispiele für Methodennamen:
getIndex()
run()
validatePassword()
split()
create()
deleteAll()
getColorInRGB()
Variablen
Für Variablennamen werden meist Substantive gewählt. Alternativ können bei
boolean-Variablen auch Adjektive gewählt werden, denen sich die Werte true oder
false zuordnen lassen.
Variablennamen werden, wie Methoden, in lower Camel Case geschrieben. Sie sollten
detailliert beschreiben, für welchen Zweck die Variable verwendet wird oder was
in ihr gespeichert ist. Allgemeine Namen, wie array oder map, sollten dagegen
nicht verwendet werden.
Die oben beschriebenen Richtlinien gelten sowohl für lokale Variablen als auch für
Parameter und Attribute (die auch als Membervariablen bezeichnet werden). Daher
stellt sich die Frage, wie diese im Code unterschieden werden können. Hier gibt es
mehrere Möglichkeiten, u. a:
1. Die Methode ist so kurz, dass sofort ersichtlich ist, um was es sich handelt
2. Für Membervariablen durchgehend this. verwenden
3. Nicht auf Membervariablen direkt zugreifen, sondern Getter und Setter
verwenden
Möglichkeit 1 ist ganz klar präferiert. Methoden sollten so kurz wie möglich gehalten werden. Bei Methoden, die Maximal 10 bis 20 Zeilen umfassen, ist meist
6.3 Coding Conventions
sehr schnell klar, ob es sich um eine Membervariable oder um eine lokale Variable
handelt.
Möglichkeit 2 bietet Sicherheit, was die Verwendung von Membervariablen angeht.
So kann es nicht passieren, dass aus Versehen eine Membervariable in Ihrem Wert
verändert wird oder man einen Wert aus einer lokalen Variable liest, die ein paar
(hundert) Zeilen zuvor definiert wurde und die man für eine Membervariable
gehalten hat.
Möglichkeit 3 ist ebenso eine gute Option. Besonders, wenn man ohnehin Getter
und Setter definiert hat, sollte man von diesen auch Gebrauch machen.
Konstanten
Mit Konstanten haben wir uns in den bisherigen Studienbriefen noch nicht beschäftigt. Konstanten sind genau genommen lediglich Membervariablen, die ihren
Wert niemals ändern.
Das beste Beispiel für eine solche Konstante ist die Zahl Pi. Um diese Zahl nicht
in jeder Formel neu definieren zu müssen, würden Sie die Kreiszahl vermutlich
als Membervariable speichern. Aber sie wird dieser Bezeichnung nicht wirklich
gerecht: Sie ist nicht variabel, sondern hat einen konstanten Wert.
Um festzulegen, dass der Wert in einer Variable niemals verändert werden kann,
wird das Schlüsselwort final verwendet. Einer Membervariablen, die als final markiert wurde, kann nach der ersten Wertzuweisung ein anderer Wert zugeordnet
werden. Folgendes ist demnach nicht möglich:
class Example
{
final int value = 1;
public Examle()
{
this.value = 2;
}
}
Im Konstruktor versuchen wir, einer final-Variablen einen neuen Wert zuzuweisen, nachdem dieser bereits ein Wert zugewiesen wurde. Dieser Code würde so
nicht kompilieren. Folgender Code dagegen ist okay:
class Example
{
final int value;
public Example()
{
this.value = 2;
}
}
Seite 15
Seite 16
Studienbrief 6 Coding Conventions und Best Practices
Hier wird der final-Variablen zum ersten Mal im Konstruktor ein Wert zugewiesen.
Dies ist ein völlig legitimer Einsatz einer solchen Variablen.
M
Merksatz 6.1: Zuweisung von final-Variablen
Der Wert einer mit final deklarierten Variable muss nach dem Anlegen des
Objekts, sprich nach dem Aufruf des Konstruktors, feststehen. Es ist also
nicht legitim, in einer Methode den Wert einer solchen Variable zu setzen.
Tun Sie es dennoch, wird der Code nicht kompilieren.
Um wieder auf Konstanten zurückzukommen: Eine Konstante wird als immer als
final deklariert. Da die Konstante immer für alle Instanzen der Klasse gilt, wird
sie zudem noch als static deklariert.
Um Konstanten von anderen Membervariablen unterscheiden zu können, hat
sich eine bei vielen Programmiersprachen folgende Notation durchgesetzt: Konstanten werden komplett in Großbuchstaben geschrieben. Wenn der Name der
Konstanten aus mehreren Wörtern besteht, so werden diese durch einen Underscore
(Unterstrich) voneinander getrennt.
Die Kreiszahl Pi würde also im Code folgendermaßen aussehen:
public final static double PI = 3.14;
Natürlich müssen Sie Pi nicht selbst definieren. Die Klasse Math bietet Zugriff
diese und andere Konstanten. In Beispiel 6.4 sehen weitere mögliche Namen für
Konstanten.
B
Beispiel 6.4: Namenskonvention bei Konstanten
Neben der Konstanten PI bietet die Klasse Math noch eine weitere Konstante:
Math.E für die eulersche Zahl.
Hier noch einige weitere Beispiel für Konstanten:
NUMBER_OF_PLANETS
INITIAL_VALUE
MIN_SIZE
MAX_SIZE
SECRET_KEY
ERROR_MARGIN
6.3 Coding Conventions
Kontrollaufgabe 6.1: Syntaxbausteine erkennen
Um welche Syntaxbausteine handelt es sich bei den folgenden Konstrukten? Bei Methoden wurden, um es nicht trivial zu machen, die Klammern
weggelassen.
run
isValid
SPEED_OF_LIGHT
Browser
file
Deletable
deleteLast
firstValue
6.3.2 Klammersetzung
Bei der Frage, wie Sie die geschweiften Klammern zu setzen haben, werden Sie auf
zwei Fraktionen stoßen. Die eine Fraktion setzt die öffnende geschweifte Klammer
in der selben Zeile wie den vorangegangenen Befehl:
public int getNumber() {
// Code with return
}
Die andere Fraktion setzt die öffnende Klammer in eine eigene Zeile. Die Einrückungstiefe ist dabei identisch mit der darüber stehenden Zeile:
public int getNumber()
{
// Code with return
}
Jede Fraktion wird Ihnen die Vor- und Nachteile ihrer Variante erklären. Im Grunde
ist aber reine Geschmackssache. Wichtig ist lediglich, sich für eine Variante zu
entscheiden und diese konstant zu benutzen. In diesen Studenbriefen verwenden
wir die zweite Variante, da der Autor diese privat benutzt.
Bei der ersten Variante gibt es eine weitere Besonderheit: Bei if-else-Anweisungen
erfolgt die öffnende Klammer des else-Teils in der selben Zeile, in der der if-Teil
beendet wurde:
if(condition) {
//Code
} else {
// other Code
}
6.3.3 Einrückung
Wie schon bei der Klammersetzung, gibt es auch bei der Einrückung von Code
mehrere Möglichkeiten. Was aber alle gemeinsam haben: Nach jeder öffnenden
geschweiften Klammer wird der Code um eine weitere „Einheit“ eingerückt.
Seite 17
K
Seite 18
Studienbrief 6 Coding Conventions und Best Practices
Unterschiede gibt es lediglich bei den Ausmaßen dieser Einheit. Viele Leute verwenden ein Tab (also die Tabulatortaste), um den Code einzurücken. Je nach Entwicklungsumgebung oder persönlichen Einstellungen bewirkt diese Taste entweder
das Einfügen eines Tabs (als Escape Sequence im Text \t geschrieben) oder einer
gewissen Anzahl an Leerzeichen.
Problematisch dabei ist, dass die Länge eines Tabs nicht fest vorgeschrieben ist.
Häufig entspricht die Länge 4 Leerzeichen, manchmal auch mehr oder weniger.
Probleme können dann auftreten, wenn diese Taste dazu genutzt wird, um Code
an eine bestimmte Stelle zu rücken, um die Leserlichkeit zu erhöhen. Wenn ein
anderer Ihren Code öffnet und eine andere Tabulatorbreite hat, ist die Formatierung
kaputt.
Aus diesem Grund machst es Sinn, direkt Leerzeichen zu verwenden und beim
Druck der Tabulatortaste entsprechend Leerzeichen einzusetzen. Aber auch hier
ist wichtiger, dass sich für eine Variante entschieden und diese konstant umgesetzt
wird.
E
Exkurs 6.1: Einrückung in Python
In der Programmiersprache Python werden keine geschweiften Klammern
genutzt, um einzelne Blöcke voneinander abzugrenzen. Stattdessen wird zu
diesem Zweck die Einrückung genutzt. Sollte die Einrückung nicht stimmen
(oder man verwendet sowohl Tabs als auch Leerzeichen), dann kann der
Code nicht ausgeführt werden.
Ein kleines Beispiel in Python:
for i in range(0,20):
print(i)
print("done")
Ohne die Einrückung von print(i) würde es zu einem Fehler kommen, da
nach einem Doppelpunkt zumindest eine Zeile eingerückt werden muss.
Um die for-Schleife zu beenden, reicht es aber, die Einrückung zu beenden.
Der Befehl print("done") wird erst nach der Schleife ausgeführt.
6.3.4 Zeilenlänge
Bei der Zeilenlänge galt früher die Devise: Eine Zeile sollte nicht mehr als 80 Zeichen haben. Grund dafür waren alte, terminalbasierte Editoren, die maximal eine
Zeilenlänge von 80 Zeichen auf dem Bildschirm darstellen konnten. Wenn die Zeile
länger war, musste man umständlich scrollen, um den Rest der Zeile zu sehen.
Heute ist dieses Problem nicht mehr so gegenwärtig: Moderne IDEs und Editoren
können beliebig viele Zeilen pro Zeile darstellen, je nach eingestellter Schriftgröße
und Auflösung.
Dennoch sollten Sie Ihre Zeilenlänge begrenzen. Bei sehr langen Zeilen kann es
auch heute noch passieren, dass diese nicht auf den Bildschirm passen. Zudem
sinkt mit zunehmender Zeilenlänge das Codeverständnis und die allgemeine
Übersicht.
6.3 Coding Conventions
Seite 19
Und auch wenn sie nicht mehr allgegenwärtig sind: Auch heute werden noch
terminalbasierte Editoren verwendet, die eine Zeilenlänge von 80 Zeichen darstellen. Besonders bei der Serverkonfiguration greift man häufig auf solche Editoren
zurück.
Sie sollten also bei einem Projekt von vornherein festlegen, welche Länge die Zeilen
maximal haben dürfen. Der Google Java Style (Google [2014]) beispielsweise schreibt
eine Länge von 80 oder 100 Zeichen vor.
Was aber, wenn die Zeile oder der Methodenaufruf mehr als diese Zeichenzahl
benötigt? Dann muss auf Zeilenumbrüche zurückgegriffen werden.
6.3.5 Zeilenumbrüche
Bei der richtigen Anwendung von Zeilenumbrüchen unterscheidet man zwischen
dem Zeilenumbruch bei Nicht-Zuweisungsoperatoren und dem Zeilenumbruch bei
Zuweisungsoperatoren.
Zu den Nicht-Zuweisungs-Operatoren gehören beispielsweise der Punkt beim
Aufruf einer Methode oder die logischen Operatoren bei einer if-Abfrage. Wenn
bei solchen Konstrukten ein Zeilenumbruch nötig ist, dann erfolgt der Umbruch
vor dem Operator. Ein Beispiel für einen solchen Zeilenumbruch finden Sie in
Beispiel 6.5.
Beispiel 6.5: Zeilenumbruch bei Nicht-Zuweisungsoperatoren
if(firstConditionMet
&& secondConditionMet)
{
redCar.getColor()
.getRGB()
.getRed();
}
In dem oberen Beispiel haben wir mehrere Zeilenumbrüche eingefügt, um
die Leserlichkeit zu erhöhen und die Zeilenlänge zu reduzieren. Beachten
Sie, dass der Zeilenumbruch vor dem jeweiligen Operator erfolgt.
Bei Zuweisungsoperatoren erfolgt der Zeilenumbruch dagegen nach dem Operator.
Außer dem Zuweisungsoperator selbst gibt es aber noch weitere Operatoren, bei
denen der Zeilenumbruch nachträglich erfolgt: So zum Beispiel das Komma oder
das Semikolon in einer for-Schleife.
B
Seite 20
Studienbrief 6 Coding Conventions und Best Practices
In Beispiel 6.6 finden Sie zur Verdeutlichung ein Codebeispiel.
B
Beispiel 6.6: Zeilenumbruch bei Zuweisungsoperatoren
public int longMethodName(int firstParameter,
int secondParameter,
int thirsParamter)
{
int longVariableName =
2 * this.getVeryLongName();
for(int i = longVariableName;
i > 0; i--)
{
// Do something
}
}
In obigem Code sind mehrere Beispiele für Zeilenumbrüche zu finden, die
nach dem Operator erfolgen. Dies tritt am Häufigsten bei dem Zuweisungsoperator sowie dem Semikolon und dem Komma auf.
Einrückung nach Zeilenumbruch
Bei der Einrückung nach einem Zeilenumbruch gibt es eine Besonderheit: Das
gebrochene Codefragment wird doppelt eingerückt. Wenn also die normale Einrückung 4 Leerzeichen beträgt, wird der Code um 8 Leerzeichen eingerückt. Auf
diese Weise hebt sich der Code besser von dem darauf folgenden Block ab.
Zeilenumbruch bei Importen und Paketnamen
Importe und Paketnamen bilden eine Ausnahme beim Zeilenumbruch. Hier darf
kein Zeilenumbruch erfolgen, da sonst der Befehl vom Compiler nicht korrekt
erkannt wird.
6.3.6 Encoding
Wenn Sie eine Textdatei speichern, wird diese auf Ihrer Festplatte in Form von Bits
und Bytes abgespeichert. Ebenso verhält es sich mit .java-Dateien, die ebenfalls
einfache Textdateien sind. Wenn Sie die Datei öffnen wollen, müssen Sie die Bits
als Buchstaben interpretieren. Diese Interpretation ist das so genannte Encoding,
auf deutsch Zeichenkodierung.
Es gibt verschiedene Möglichkeiten, eine Zahl (also mehrere Bits) als Buchstaben
zu interpretieren bzw. eine Buchstaben als Bits zu speichern. Geläufig ist die ASCIIKodierung. ASCII umfasst 127 Zeichen, wandelt also 8 Bits in einen Buchstaben um.
Problem hierbei: Das reicht bei weitem nicht aus, um alle Sprachen, u. a. deutsch,
abzubilden. Mit ASCII lassen sich beispielsweise keine Umlaute darstellen.
Wir wollen an dieser Stelle nicht zu tief in die Thematik des Encoding eintauchen.
Es reicht aus, wenn Sie sich an folgende Regel halten: Benutzen Sie immer UTF-8
als Encoding für Ihre Dateien. So können Sie alle geläufigen Zeichen in allen
6.3 Coding Conventions
Seite 21
Sprachen (auch Klingonisch) in Ihrem Code verwenden. Beachten Sie aber hierzu
noch Merksatz 6.2.
Merksatz 6.2: Encoding
M
Nutzen Sie für die Benennung von Variablen, Klassen usw. nur Buchstaben,
die im ASCII-Encoding vorkommen. Im Grunde sind dies die Buchstaben
von A bis Z (klein und groß), die Zahlen von 0 bis 9, sowie Sonderzeichen
wie der Underscore.
Bei Strings und Kommentaren bleibt es Ihnen überlassen, welche Sprache
Sie verwenden. Lesen Sie dazu den nächsten Abschnitt.
Exkurs 6.2: Was jeder über Encoding wissen sollte
E
Der Softwareentwickler und Blogger Joel Spolsky hat 2003 den Artikel The
Absolute Minimum Every Software Developer Absolutely, Positively Must Know
About Unicode and Character Sets (No Excuses!) verfasst (Spolsky [2003]). Lesen
Sie diesen Artikel2 , wenn auch Sie das absolute Minimum über Encoding
wissen wollen.
Kontrollaufgabe 6.2: Encoding
K
Legen Sie eine neue Textdatei an, schreiben Sie einige Umlaute in diese
Datei und speichern Sie dann mit einem beliebigen Encoding außer UTF-8
ab. Öffnen Sie nun die Datei mit einem anderen Texteditor. Wird Ihnen der
Text korrekt angezeigt?
6.3.7 Sprache
Zur Sprache, in dem der Code verfasst wird, gibt es nicht viel zu sagen: Da die
Befehle auf Englisch sind, sollten Ihre Namen für Variablen, Methoden usw. ebenfalls aus der englischen Sprache stammen. Auf keinen Fall sollte eine Mischform
wie getFarbe oder setWert gewählt werden!
Exkurs 6.3: T_PAAMAYIM_NEKUDOTAYIM (entnommen aus Passig
[2013])
Bei der PHP-Programmierung konnte es passieren, dass Sie auf den Fehler
error: syntax error, unexpected T_PAAMAYIM_NEKUDOTAYIM
gestoßen sind. Sie sehen wahrscheinlich aufgrund der Schreibweise, dass
es sich um eine Konstante handeln muss. Wenn Sie aber nicht des hebräischen mächtig sind, werden Sie wohl nicht darauf kommen, dass hier zwei
aufeinanderfolgende Doppelpunkte angeprangert werden.
2 http://www.joelonsoftware.com/articles/Unicode.html
E
Seite 22
Studienbrief 6 Coding Conventions und Best Practices
6.4 Dokumentation
Beim Programmieren verbringen Sie gewöhnlich mehr Zeit damit, Code zu lesen,
als selbst Code zu verfassen. Durch die bisherigen Coding Conventions können Sie
imemrhin sicherstellen, dass ein anderer Programmierer Ihren Code strukturell
nachvollziehen kann und sich nicht komplett „fremd“ fühlt.
Um den Code aber in seiner Gänze nachvollziehen zu können, sind Kommentare
und vor allem eine ausführliche Dokumentation notwendig.
6.4.1 Kommentare
In Studienbrief 2 haben Sie bereits gelernt, wie in Java Kommentare formuliert
werden können: Einzeilige Kommentare werden mit einem doppelten Slash (//)
eingeleitet. Mehrzeilige Kommentare beginnen mit /* und enden mit */.
// one line commentary
/*
multi
line
commentary
/
*
Wie aber können Sie Kommentare schreiben, die anderen Programmierern und
Ihnen selbst helfen, den Code besser zu Verstehen?
Zunächst eine Grundregel: Sie können davon ausgehen, dass derjenige, der Ihren
Code betrachtet, die Programmiersprache beherrscht. Das bedeutet, dass Sie nicht
Offensichtliches kommentieren müssen. Folgendes Beispiel ist also unsinnig:
int value = 5; // sets value to 5
Jeder Programmierer erkennt sofort, was sie hier tun. Dafür benötigt er keinen
Kommentar. Viel wichtiger, als zu beschreiben, was der Code tut, ist es, zu beschreiben, warum der Code sich so verhält. Folgendes ist also durchaus als Kommentar
angebracht:
int value = 5; // number of fingers
Dieses Beispiel führt uns zu einer weiteren, wichtigen Grundregel: Guter Code
dokumentiert sich selbst. Durch die geschickte Namenswahl bei Variablen und Methoden sowie durch Refactoring und kurzen Methoden bedarf es häufig keinem
Kommentar. Anstatt also zu kommentieren, dass eine Hand 5 Finger hat, schreiben
Sie Folgendes:
int numberOfFingers = 5;
Durch die Wahl eines aussagekräftigen Namens erübrigt sich ein Kommentar.
Bevor wir uns weiter mit Kommentaren und Dokumentation befassen, müssen wir
eine Unterscheidung treffen. Einerseits gibt es die Möglichkeit, durch Kommentare
unseren Code verständlicher zu machen. Anderseits gibt es die Möglichkeit, eine
ausführliche Dokumentation zu schreiben.
6.4 Dokumentation
Seite 23
Kommentare im Quelltext
Was wir bisher betrachtet haben, waren Kommentare direkt im Quelltext. Sie
werden direkt an die Stelle geschrieben, auf die sie Bezug nehmen.
Diese Kommentare sind für andere Entwickler bestimmt, die Ihren Quelltext
unkompiliert vorliegen haben. Ziel dieser Gruppe ist es, den Ablauf innerhalb Ihres
Codes zu verstehen, um beispielsweise Änderungen vorzunehmen oder Fehler
auszubessern.
Die Kommentare sollten sie dabei unterstützen: Beschreiben Sie keine Trivialitäten,
sondern ergänzen Sie den Code um wertvolle Hinweise, die zum Verständnis beitragen. Bei tief verschachtelten Methoden können Sie beispielsweise das Verständnis
erhöhen, indem Sie bei schließenden Klammern notieren, welche for-Schleife gerade geschlossen wurde. Bei komplizierten if-Bedingungen können Sie in einfacher
Form schreiben, welche Bedingung abgeprüft wird. Aber auch hier gilt: Guter
Code dokumentiert sich selbst. Anstatt also komplizierte Dinge mit Kommentaren zu
versehen, sollten Sie immer versuchen, den Code zu vereinfachen und besser zu
strukturieren.
Bevor Sie also bei einer if-Abfrage den Kommentar „Checks if user is logged in“
schreiben, sollten Sie die Abfrage lieber in eine eigene Methode mit dem Namen
isLoggedIn auslagern.
Exkurs 6.4: Tasks in Eclipse
E
Eclipse hat ein nützlicher Feature, um Stellen im Code zu markieren, an
denen noch einige Dinge zu tun sind. Wenn Sie in Ihrem Code einen Kommentar mit // TODO: beginnen, so wird dieser Kommentar gesondert hervorgehoben. Wenn Sie nun die View Tasks öffnen (Window → Show View
→ Tasks), sehen Sie alle TODOs in einer Liste.
Neben TODO gibt es noch weitere „Tags“, wie FIXME: oder XXX:.
Beispiel 6.7: Zweierpotenz
Folgendes Codefragment stammt aus dem Sourcecode der Klasse Random3
if ((n & -n) == n) // i.e., n is a power of 2
Dies ist ein sehr gutes Beispiel für einen Kommentar, der dem Leser beim
Verständnis des Codes hilft. Oder wären Sie direkt darauf gekommen, dass
hier geprüft wird, ob n eine Zweierpotenz ist?
Man könnte sich natürlich überlegen, diese Abfrage als eigene Methode
isPowerOfTwo auszulagern.
B
Seite 24
K
Studienbrief 6 Coding Conventions und Best Practices
Kontrollaufgabe 6.3: Zweierpotenz
Versuchen Sie nachzuvollziehen, warum folgender Code abprüft, ob n eine
Zweiterpotenz ist:
if ((n & -n) == n) // i.e., n is a power of 2
Sie müssen es nicht mathematisch nachweisen, sondern einfach logisch
nachvollziehen können. Beachten Sie, dass durch -n das Zweiterkomplement
gebildet wird.
Javadoc
Neben der Möglichkeit, Code für andere Entwickler zu kommentieren, gibt es bei
Java von Haus aus die Möglichkeit, ausführliche Dokumentationen komfortabel zu
schreiben. Durch eine spezielle Notation und das Tool javadoc können aus diesen
Dokumentationen HTML-Seiten generiert werden, wie Sie sie von der Java-API
kennen.4
Die Dokumentation mittels javadoc ist zum einen für Sie und andere Entwickler
gedacht. Zum anderen ist diese Dokumentation aber auch für Nutzer Ihrer Software
gedacht, denen der Quellcode nicht vorliegt.
Das beste Beispiel hierfür ist die Java-API. Ohne sich mit dem Quellcode zu befassen, können Sie durch die Dokumentation leicht feststellen, was eine Methode tut.
Dafür können Sie entweder die Dokumentation auf der Webseite lesen oder aber
direkt in Ihrer IDE ansehen (falls diese dies unterstützt).
Bevor wir detailliert auf javadoc eingehen, betrachten wir den Code in Quelltext 6.1.
Q
Quelltext 6.1: "Javadoc"
1
/**
* short Description
3 * <p>
4 * Here is a long, detailed description.
2
5
6
7
8
9
10
11
12
13
*
* @param numberOfTimes How often something is done.
If negative, nothing happens
*
@param
what What should be done. If null, nothing happens.
*
/
**
public void doSomething ( int numberOfTimes , String what )
{
// code
}
In dem oberen Quelltext ist die Methode doSomething mittels Javadoc dokumentiert. Um einen Javadoc-Kommentar zu beginnen, wird ein Slash mit zwei Sternen
(/**) genutzt. Die Kombination von zwei Sternen, gefolgt von einem Slash (**/),
3 http://developer.classpath.org/doc/java/util/Random-source.html
4 http://docs.oracle.com/javase/7/docs/api/
6.4 Dokumentation
Seite 25
beendet den Javadoc-Kommentar. Jede Zeile des Kommentars beginnt mit einem
Stern (*).
Wenn Sie das javadoc-Tool über diesen Code laufen lassen, erhalten Sie eine Dokumentation im HTML-Format. Abbildung 6.1 zeigt die Übersicht über alle Methoden, wie Sie in der Dokumentation zu finden ist. Hier finden Sie auch unsere
gerade dokumentierte Methode doSomething().
Abb. 6.1: Javadoc: Zusammenfassung aller
Methoden.
Method Summary
Methods
Modifier and Type
Method and Description
void
doSomething (int numberOfTimes, java.lang.String what)
short Description
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait,
wait
Sie sehen als Beschreibung der Methode den Text „Short Description“. Diesen
finden Sie auch in Quelltext 6.1 als erste Zeile unserer Dokumentation wieder.
Generell sollten Sie immer eine Kurzbeschreibung verfassen, die dann in der Method
Summary angezeigt wird. Um diese Beschreibung zu beenden, können Sie das
HTML-Tag <p> nutzen, wie wir es im Quelltext getan haben.
Neben den Methoden, die Sie für eine Klasse selbst definiert und dokumentiert
haben, sehen Sie in der Method Summary auch die Methoden, die von der Oberklasse
geerbt wurden.
Neben der Zusammenfassung der Methoden gibt es in der HTML-Dokumentation
noch den Abschnitt Method Detail. Hier finden Sie zu jeder Methode die detaillierte
Dokumentation. Abbildung 6.2 zeigt die von uns verfasste Dokumentation der
Methode doSomething.
Method Detail
doSomething
public void doSomething(int numberOfTimes,
java.lang.String what)
short Description
Here is a long, detailed description.
Parameters:
numberOfTimes How often something is done. If negative, nothing happens
what What should be done. If null, nothing happens.
Neben der kurzen Beschreibung finden Sie auch die lange Beschreibung, die wir
im Quelltext eingetragen haben. Des Weiteren bietet Javadoc über so genannte
Tags einige nützliche Funktionen. Durch den Tag @param haben wir die Parameter,
die der Funktion übergeben werden, genauer dokumentiert. Nach dem Tag folgt
Abb. 6.2: Javadoc: Details
einer Methode.
Seite 26
Studienbrief 6 Coding Conventions und Best Practices
hier zunächst der Name des Parameters (numberOfTimes bzw. what). Darauf folgt
die Beschreibung des Parameters. Hier können Sie nun näher beschreiben, was
übergeben werden soll und wie sich der Code bei gewissen Eingaben verhält.
Was in der HTML-Dokumentation aus den @param-Tags geworden ist, sieht man in
Abbildung 6.2. Unter Paramters sind hier alle Parameter mitsamt ihrer Dokumentation zusammengefasst.
Wo kann Javadoc verwendet werden?
Bisher haben wir nur betrachtet, wie eine Methode mittels Javadoc dokumentiert
werden kann. Neben Methoden können aber noch folgende Dinge mit Hilfe von
Javadoc dokumentiert werden:
• Klassen
• Interfaces
• Konstruktoren
• Membervariablen
• Konstanten
Beachten Sie, dass innerhalb einer Methode kein Javadoc verwendet werden kann!
Falls Sie innerhalb einer Methode etwas kommentieren oder dokumentieren möchten, nutzen Sie Kommentare (siehe dazu auch Abschnitt 6.4.1 auf Seite 22).
Tags
Neben dem @param-Tag gibt es noch weitere Tags, die Sie in Javadoc verwenden
können. Manche davon sind auf bestimmte Konstrukte, wie Klassen oder Methoden, beschränkt. Tabelle 6.4.1 bietet Ihnen eine Übersicht über die die wichtigsten
Tags.
Tabelle 6.1: Die wichtigsten Javadoc-Tags
Wo verwendet
Methoden,
Konstruktoren
Syntax
Bedeutung
@param
<Parametername>
<Beschreibung>
Zur Beschreibung
von Parametern
@author
Klasse,
Interface
@author
<Autorname>
@version
Klasse,
Interface
Um den Autor einer
Klasse zu ermitteln.
Gut, wenn um einen
Ansprechpartner zu finden.
@return
Methode
@version
<Versionsnummer>
@return
<Beschreibung>
@see
Überall
Tag
@param
@see <Referenz>
Die Version der Klasse.
Zur Beschreibung
des Rückgabewertes
Um auf andere Klassen oder
Methoden zu verweisen.
Sollten sich diese in anderen
Dateien befinden, muss
eine Raute genutzt werden,
um den „Pfad“ anzugeben:
Klassenname#Methodenname
6.5 Best Practices
Seite 27
Eine komplette Liste der Tags können Sie auf der Wikipedia-Seite von Javadoc
ansehen.5
Was schreibe ich in eine Dokumentation?
Wir haben uns jetzt ausführlich damit befasst, wie eine Dokumentation mit Javadoc
geschrieben wird. An dieser Stelle gehen wir kurz auf den Inhalt einer Dokumentation ein.
Die Dokumentation sollte beschreiben, was die Methode (oder Klasse, Interface
usw.) tut. Dabei sollte die Beschreibung aber allgemein gehalten werden. Wichtig
ist, auf eventuelle Ausnahmen und Sonderfälle einzugehen. Was passiert, wenn eine
negative Zahl übergeben wird? Was bedeutet der Rückgabewert? Solche Dinge
sollten für den Nutzer Ihrer Software dokumentiert werden.
Es sollte auch darauf eingegangen werden, warum sich z. B. eine Methode so verhält.
Gibt es Eingaben, die einen Fehler auslösen? Falls Sie den Fehler nicht beseitigen
können, sollten Sie ihn zumindest dokumentieren.
Es ist auch die Unterscheidung zu treffen, für welche Zielgruppe Sie die Dokumentation schreiben. Sie sollten zunächst immer sich selbst als Zielgruppe einschließen!
Denn wenn Sie Ihren eigenen Code nach einigen Wochen/Monaten betrachten,
werden Sie kaum noch Erinnerungen an Details der Implementierung haben.
Als weitere Zielgruppen gibt es Nutzer, denen der Sourcecode nicht vorliegt. Hier
sollte die Dokumentation so geschrieben werden, dass eindeutig ist, wie sich Ihr
Code verhält. Durch die Dokumentation mit Javadoc haben Nutzer die Möglichkeit,
Ihre Dokumentation direkt in der IDE zu lesen, wenn Sie den Mauszeiger über die
Methode bewegen.
Ebenfalls als Zielgruppe zu betrachten sind Kollegen oder andere Entwickler, denen Ihr Quellcode vorliegt. Diese Personen wollen häufig Ihren Quelltext verstehen
und verändern, sodass die Dokumentation Sie bei diesem Vorhaben unterstützen
sollte.
Beispiel 6.8: Dokumentation der Java-API
Betrachten Sie die Dokumentation und den Quelltext der Klasse Random.6
Die gesamte Klasse ist ausführlich mit Javadoc dokumentiert. Wo nötig, sind
im Code selbst noch kleine Anmerkungen. Die Länge der Dokumentation
übersteigt den eigentlichen Quelltext deutlich.
6.5 Best Practices
Auf den nächsten Seiten werden wir Ihnen einige Best Practices für Java vorstellen.
Hierunter verstehen wir gewisse Vorgehensweisen, Methodiken und Denkweisen,
die Ihnen helfen, besseren Code zu schreiben.
5 http://de.wikipedia.org/wiki/Javadoc
6 http://developer.classpath.org/doc/java/util/Random-source.html
B
Seite 28
Studienbrief 6 Coding Conventions und Best Practices
6.5.1 Testing
Dieser Punkt ist so wichtig, dass wir ihm im letzten Semester einen kompletten
Studienbrief gewidmet haben. Sie sollten Ihren Code immer testen, bevor Sie ihn
ausliefern, abgeben oder in einem Programm verwenden.
Das muss nicht unbedingt heißen, dass Sie mittels JUnit Tests schreiben. Ihren
Code in der main-Methode zu testen, ist bereits ein Anfang. Das Problem an der
main-Methode ist aber, dass Sie den Code dort immer wieder löschen und umschreiben werden. Wie können Sie da sicher gehen, dass nach einer Änderung
alles noch so funktioniert, wie bisher? Die bessere Lösung ist daher, von Anfang
an Junit zu verwenden.
Der Vorteil davon liegt auf der Hand: Sie können die Tests immer wieder verwenden. Sie haben die Sicherheit, dass ihr Code für die im Test benutzten Parameter
korrekt funktioniert. Wenn Sie Änderungen am Code vornehmen, haben Sie die
Gewissheit, dass alle Funktionen erhalten geblieben sind.
Damit bilden Tests die theoretische Grundlage für das Refactoring, mit dem wir
uns im nächsten Abschnitt beschäftigen.
Wann sollte ich Tests schreiben?
Wichtig ist, dass Sie überhaupt Tests schreiben. Es gibt die Möglichkeit, die Tests vor
dem zu testenden Code zu schreiben. Im Anschluss schreiben Sie den minimalen
Code, der erforderlich ist, um die Tests zu bestehen. Dieses Vorgehen wird auch
als Test Driven Development bezeichnet.
Sie können die Tests natürlich auch erst schreiben, nachdem Sie den zu testenden
Code geschrieben haben. Hier macht einem aber häufig die eigene Bequemlichkeit
zu schaffen: Der Code, auf den es ankommt, existiert bereits. Warum sollen Sie
Ihre Zeit damit verschwenden, Code zu schreiben, der Sie bei der Lösung eines
Problems nicht voran bringt? Aus diesen und anderen Gründen wird das Schreiben
von Tests gerne nach hinten verschoben und eventuell ganz vergessen.
Tests für den gesamten Code zu schreiben, kostet am Anfang häufig Überwindung.
Sie werden sich manchmal fragen, warum jede Kleinigkeit getestet werden muss.
Wenn jedoch nach dem Refactoring plötzlich ein Test fehlschlägt, werden Sie für
diesen Test dankbar sein.
6.5.2 Refactoring
Refactoring gehört zum Entwicklungsprozess dazu. Zur Erinnerung: Unter Refactoring versteht man das Verbesserern des Codes (Struktur, Laufzeit, Leserlichkeit)
ohne die Funktion zu verändern. Nach dem Refactoring sollte das Programm also
das selbe tun wie vorher.
Und hier kommen wir an den Punkt, den wir im letzten Abschnitt angesprochen
haben: Wie können Sie sicher sein, dass sich Ihr Programm genau so verhält wie
vorher? Wenn Sie keine Tests haben, müssten Sie theoretisch alles noch einmal von
Hand testen. Automatische Tests sind somit theoretisch eine Notwendigkeit für
erfolgreiches und effektives Refactoring.
Wie gehe ich beim Refactoring vor?
Diese Frage lässt sich nicht gezielt beantworten. Generell sollten Sie, nachdem Sie
eine Methode oder Klasse implementiert haben, sich Ihren Code erneut ansehen.
6.5 Best Practices
Seite 29
Erfüllt Ihr Code die Coding Conventions? Sind Best Practices, wie Sie in den folgenden Abschnitten vorgestellt werden, berücksichtigt? Lässt sich das Problem
effektiver lösen? Kann ich Methoden auslagern oder sind manche Methoden in anderen Paketen/Klassen besser aufgehoben? Diese Fragen sollten Sie im Hinterkopf
haben, wenn Sie sich Ihren Code ansehen.
Generell muss man aber sagen: Es gibt nicht den perfekten Code. Refactoring sieht
bei jedem Programmierer anders aus. Beherzigen Sie einfach Folgendes:
Always code as if the person who ends up maintaining your code is a violent
psychopath who knows where you live.7
Vergessen Sie beim Refactoring nicht, die Dokumentation anzupassen!
6.5.3 Don’t repeat yourself (DRY)
Die Regel Don’t repeat yourself ist eine Regel, die Sie auf jeden Fall beherzigen sollten.
Folgendes ist damit gemeint: Wenn an mehreren Stellen in Ihrem Code exakt die
selben (oder sehr ähnliche) Befehle stehen, dann ist es an der Zeit für Refactoring.
Sie sollten den gemeinsamen Code dann in eine eigene Methode auslagern.
Dabei können Sie auf eine weitere Regel zurückgreifen: Die Rule of three. Sie besagt,
dass wenn Sie den selben Code an drei verschiedenen Stellen im Programm nutzen,
Sie diesen Code in eine Methode auslagern sollten. Einmaliges Kopieren ist okay,
aber bei der zweiten Kopie (also dem dritten Auftreten) sollten Sie Refactoring
betreiben.
Generell weist die Verwendung von Copy-Paste darauf hin, dass an dieser Stelle
Refactoring angebracht ist. Beim Kopieren von Code sollten Sie immer äußerste
Vorsicht walten lassen. Man vergisst gerne einmal, eine Variable umzubenennen
oder den Code überall abzuändern.
Beispiel 6.9: DRY: Matrixoperationen
Erinnern Sie sich noch an die Aufgabe aus dem letzten Semester, in der Sie
Methoden geschrieben haben, die Matrixoperationen durchführen?
Vermutlich haben Sie zu diesem Zeitpunkt die komplette Logik in jeweils
eine Methode geschrieben. Aber wenn man sich die einzelnen Methoden
(add und multiply) betrachtet, findet man Gemeinsamkeiten:
Bei beiden Methoden müssen die übergeben Matrizen „rechteckig“ sein.
Bei beiden Methoden dürfen die Matrizen nicht leer sein, also in einer
Dimension die Ausmaße 0 haben.
Bei diesen Abfragen handelt es sich eindeutig um Methoden, die man auslagern kann. Sie könnten also eine Methode isRectangular() und eine
Methode isEmpty() schreiben, die diese Abfragen vornehmen.
7
Genaue Herkunft nicht bekannt. Eventuell John F. Woods.
B
Seite 30
Studienbrief 6 Coding Conventions und Best Practices
6.5.4 Methods should do one thing (nach Martin [2008]
Eine weitere wichtige Regel, die Sie beherzigen sollten, ist die folgende: Eine
Methode sollte immer nur eine konkrete Logik (oder einen konkreten Algorithmus)
implementieren. Wenn Ihre Methode mehrere Dinge implementiert, sollten Sie
Refactoring betreiben und die Methode in einzelne Methoden aufteilen.
Das Originalzitat, das dieser Regel voraus geht, stammt aus dem Buch Clean Code:
A Handbook of Agile Software Craftsmanship von Robert C. Martin:
Functions should do one thing. They should do it well. They should do it only.
— Martin [2008]
Es ist selbstverständlich in Ordnung, wenn in Ihrer Methode weitere Methoden
aufgerufen werden, die unterschiedliche Dinge tun. Dies lässt sich beim Programmieren auch gar nicht vermeiden. Die Regel bezieht sich auf die Implementierung
unterschiedlicher Dinge in der selben Methode.
Wie kann man feststellen, ob sich eine Methode an diese Regel hält? Hierzu können
Sie sich folgende Frage stellen: „Gibt es in meiner Methode einzelne Abschnitte,
die sich konkret benennen kann?“ Falls Sie diese Frage mit ja beantworten können,
dann können Sie den entsprechenden Code in eine eigene Methode auslagern.
B
Beispiel 6.10: One method, one thing: Matrixoperationen
Betrachten wir wiederum die Aufgabe, Matrixoperationen zu implementieren. Zunächst nur die add-Methode:
public static int[][] add(int[][] first,
int[][] second)
{
if ( !(first != null && second != null
&& first.length > 0
&& first.length == second.length
&& first[0].length > 0
&& second[0].length > 0))
{
return null;
}
int numRows = first.length;
int numCols = first[0].length;
// check if every row has same length
// length should be identical to length of first
row
for(int i = 0; i<first.length; i++)
{
if (first[i] != numCols || second[i] != numCols)
{
return null;
}
}
6.5 Best Practices
// add matrizes
int[][] result = new int[numRows][numCols];
for (int row = 0; row < numRows; row++)
{
for (int col = 0; col < numCols; col++)
{
result[row][col] = first[row][col]
+ second[row][col];
}
}
return result;
}
Der Code hat einige offensichtliche Schwachstellen: Er ist relativ lang, es
gibt sehr viele Abfragen, es existiert keine Dokumentation und man ist
nicht sicher, warum manche Abfragen gemacht werden. Was ebenso auffällt:
Die Methode heißt add, sollte also zwei Matrizen addieren. Hauptanteil
an der Methode hat aber der Code, der prüft, ob sich diese zwei Matrizen
überhaupt addieren lassen.
Wir können diesem Codeteil einen konkreten Namen geben: canAddMatrizes
(Oder ein ähnlicher Name). Wir sollten diesen Code daher auslagern:
public static int[][] add(int[][] first,
int[][] second)
{
if (!(canAddMatrizes(first, second)))
{
return null;
}
int numRows = first.length;
int numCols = first[0].length;
int[][] result = new int[numRows][numCols];
for (int row = 0; row < numRows; row++)
{
for (int col = 0; col < numCols; col++)
{
result[row][col] = first[row][col]
+ second[row][col];
}
}
return result;
}
Allein durch diese Ausgliederung ist der Code deutlich übersichtlicher
geworden. Die Methode implementiert nun wirklich nur die Addition selbst
– die Abfrage, ob addiert werden kann, ist ausgelagert.
Seite 31
Seite 32
Studienbrief 6 Coding Conventions und Best Practices
An dieser Stelle ist aber nicht Schluss: In der Methode canAddMatrizes
sind ebenfalls Kandidaten, denen man eine eigene Methode geben kann.
Überlegen Sie dafür, was genau abgeprüft wird:
• Ob die Matrizen rechteckig sind (also in jeder Zeile die gleiche Anzahl
von Einträgen ist)
• Ob die zwei Matrizen die gleichen Dimensionen haben
• Ob die Matrizen in jeder Dimension einen Eintrag haben (also keine
Spalten/Zeilen mit Länge 0)
Die Methode canAddMatrizes kann also folgendermaßen aussehen:
private static boolean canAddMatrizes(int[][] first,
int[][] second)
{
if(first != null && second != null
&& isRectangular(first)
&& isRectangular(second)
&& areSameDimensions(first, second)
&& hasEntryInEveryDimension(first)
&& hasEntryInEveryDimension(second))
{
return true;
}
else
{
return false;
}
}
Dieser Code lässt sich deutlich einfacher lesen als der bisherige, da man
direkt erkennt, was abgefragt wird und welche Voraussetzungen für die
Addition notwendig sind.
K
Kontrollaufgabe 6.4: MatrixOperation: Multiply
Betrachten Sie Ihren eigenen Code bzw. konkret Ihre eigene Implementierung der Methode multiply. Führen Sie ein Refactoring durch, indem Sie
Methoden auslagern und ggf. Variablen umbenennen. Prüfen Sie auch, ob
es Gemeinsamkeiten zur Methode add gibt.
6.5.5 Magic Numbers
Magic Numbers sind Nummern, die in Methoden auftauchen. Ihr Zweck bzw. ihre
Herkunft sind nicht immer sofort eindeutig. Solche Nummern werden gerne als
Kodierung benutzt, aber auch für z. B. für Einstellungen. Als Beispiel soll die
Überprüfung einer Passwortlänge dienen:
public boolean isValidPassword(String password)
{
if (password.length() >= 8)
{
6.5 Best Practices
Seite 33
return true;
}
else
{
return false;
}
}
Bei der Zahl 8 handelt es sich um eine typische Magic Number. Die Zahl scheint es
der Luft gegriffen. Wenn man die valide Passwortlänge anpassen möchte, muss
man erst umständlich im Code suchen. Es kann auch noch schlimmer sein: Die
Passwortlänge kann an mehreren Stellen im Code auftauchen. So könnten wir
beim Anpassen der Länge theoretisch eine Zahl vergessen.
Solche Nummern sollten immer als Konstanten gesetzt werden. So ist es möglich,
schnelle Anpassungen an der Passwortpolicy vorzunehmen, ohne den gesamten
Code zu durchsuchen:
private static final int MIN_PASSWORD_LENGTH = 8;
// ...
public boolean isValidPassword(String password)
{
if (password.length() >= MIN_PASSWORD_LENGTH)
{
return true;
}
// etc.
}
6.5.6 Strings bauen mit StringBuilder
Es kommt oft vor, dass man für Anwendungen im Terminal Strings aneinanderreiht,
um eine große Zeichenkette zu erzeugen. Als Beispiel betrachten wir eine Methode,
die ein übergebenes Array in folgender Schreibweise darstellt: [1,2,4,8]. Den
Code finden Sie in Quelltext 6.2.
Quelltext 6.2: "Die Methode arrayToString()"
1
2
3
4
5
6
7
8
9
10
11
12
public String arrayToString(int[] array)
{
String arrayAsString = "[";
for(int i = 0; i < array.size; i++)
{
if (i != 0)
{
arrayAsString = arrayAsString + ",";
}
arrayAsString = arrayAsString + array[i];
}
arrayAsString = arrayAsString + "]";
13
14
15
return arrayAsString;
}
Q
Seite 34
Studienbrief 6 Coding Conventions und Best Practices
Der obere Quelltext ist korrekt und funktioniert ohne Einschränkungen. Das Problem, das wir an dieser Stelle ansprechen wollen, hat etwas mit dem Arbeitsspeicher zu tun. Ein String-Objekt ist immutable (dt. : unveränderbar. Der Wert bzw. die
Zeichenkette, die in einem String-Objekt gespeichert ist, kann sich, nachdem Sie
festgelegt wurde, nicht mehr verändern.
Jedes mal, wenn wir mit dem Zuweisungsoperator dem String einen neuen Wert
zuweisen, wird ein komplett neues Objekt anlegt. Dies führt natürlich zu einem
erhöhten Rechenaufwand, da der Inhalt des String-Objekts immer kopiert werden
muss.
Wenn Sie eine Zeichenkette bauen möchten, sollten Sie dazu dazu Klasse StringBuilder verwenden. Diese verfügt (unter anderem) über eine Methode append, mit der Sie Zeichenketten direkt anhängen können.
StringBuilder ist nicht immutable, sodass die Laufzeit besser ist und nicht unnötig
Objekte angelegt werden. Wenn Sie am Ende das StringBuilder-Objekt in einen
String umwandeln möchten, nutzen Sie die Methode toString().
In Quelltext 6.3 finden Sie wiederum die Methode arrayToString(), diesmal unter
Verwendung von StringBuilder.
Q
Quelltext 6.3: "Die Methode arrayToString() unter Verwendung von
StringBuilder"
1
2
3
4
5
6
7
8
9
10
11
12
public String arrayToString(int[] array)
{
StringBuilder arrayAsString = new StringBuilder("[");
for(int i = 0; i < array.size; i++)
{
if (i != 0)
{
arrayAsString.append(",");
}
arrayAsString.append(array[i]);
}
arrayAsString.append("]");
13
14
15
K
return arrayAsString.toString();
}]
Kontrollaufgabe 6.5: Immutable Strings
Was glauben Sie: Wie wird verhindert, dass der Wert eines Strings nach der
ersten Zuweisung verändert werden kann?
6.5.7 Wenn es sicher sein soll: SecureRandom
Verwenden Sie niemals die Klasse Random, im kritische Abschnitte oder Verfahren durch Zufallszahlen sicher zu machen. Benutzen Sie stattdessen immer die
Klasse SecureRandom.8
8 https://docs.oracle.com/javase/8/docs/api/java/security/SecureRandom.html
6.5 Best Practices
Seite 35
Wenn Sie der Hintergrund interessiert, können Sie Exkurs 6.5 lesen.
Exkurs 6.5: SecureRandom und Random
Was genau ist der Unterschied zwischen SecureRandom und Random? Warum
ist SecureRandom besser? Wir werden in diesem Exkurs kurz versuchen,
diese Fragen zu beantworten.
Zunächst einmal sollten Sie wissen, wie die Zufallszahlen in Java zustande
kommen. Grundlage hierzu ist ein so genannter Seed. Dieser wird genutzt,
um die Zufallszahlen zu berechnen. Wenn ein Angreifer den seed kennt,
kann er Ihre Zufallszahlen selbst errechnen.
Sie können zum Testen bei einer Instanz der Klasse Random den seed selbst
angeben (siehe API) und zwei Zufallszahlen ausgeben lassen. Wenn Sie
das Programm erneut aufrufen, werden exakt die selben „Zufallszahlen“
berechnet. Wenn Sie selbst keinen seed angeben, nutzt der Konstruktor von
Random die aktuelle Systemzeit in Millisekunden.
Um aus dem seed eine Zufallszahl zu generieren, wird ein Generator genutzt.
Wir gehen an dieser Stelle nicht tiefer darauf ein, wie so ein Generator
funktioniert.
Wichtig ist aber: Bei der Klasse Random wird zum Erzeugen der Zufallszahlen
ein linearer Kongruenzgenerator genutzt. Dieser ist für sicherheitskritische
Anwendungen absolut ungeeignet, da anhand von nur zwei Zufallszahlen
der seed in relativ kurzer Zeit berechnet werden kann.
Die Klasse SecureRandom erzeugt kryptographisch starke Zufallszahlen, die
in kritischen Anwendungen verwendet werden können.
Für weitere Details können Sie einen Blogeintrag von James Roper (Roper
[2010]) lesen, der das Thema kurz und leicht verständlich erklärt.9
6.5.8 Eindeutigkeit
Seien Sie in Ihrem Code immer so spezifisch wie möglich. Vermeiden Sie Mehrdeutigkeiten und Interpretationsspielraum. Betrachten wir dazu ein kurzes Beispiel:
public class Human
{
private int height;
public void setHeight(int height)
{
this.height = height;
}
}
Das Problem an diesem Code ist: Es ist nicht zu erkennen, in welcher Einheit die
Größe des Menschen gespeichert ist. Es könnten Meter, Zentimeter, Millimeter oder
Zoll sein. Ein Nutzer, der die Methode setHeight() aufruft, kann nicht wissen, in
welcher Einheit die Höhe übergeben werden muss.
Ihr erster Gedanke könnte sein, die Einheit als Kommentar einzufügen:
E
Seite 36
Studienbrief 6 Coding Conventions und Best Practices
private int height; // in cm
Diese Lösung hilft vielleicht Ihnen selbst und anderen Programmierern weiter, aber nicht dem Nutzer Ihres Codes. Dieser sieht weiterhin nur die Methode setHeight().
Durch geschickte Variablen- und Parameterbenennung können Sie dieses Problem
lösen:
private int heightInCm;
public void setHeight(int heightInCm)
// ...
Durch diese Benennung ist eindeutig, in welcher Einheit die Größe gespeichert
ist und was der Methode übergeben werden muss. Es gibt allerdings noch eine
bessere Lösung:
public void setHeightInCm(int heightInCm)
{
// ...
}
Auf diese Weise erkennt der Nutzer bereits anhand des Methodennamens, in
welcher Einheit die Größe übergeben werden muss. Zudem gibt diese Lösung
Ihnen die Möglichkeit, weitere Methoden zu implementieren, wie beispielsweise setHeightInMm(). Die interne Speicherstruktur ist so vor dem Nutzer versteckt.
6.5.9 Das Rad nicht neu erfinden
Bei einem größeren Projekt ist man immer versucht, jeden Teil des Programms
selbst zu schreiben. Stellen Sie sich einmal vor, Sie möchten eine Webseite programmieren, die einen Chat, ein Forum und einen kleinen Shop bietet.
Wenn Sie dieses Projekt angehen, um etwas zu lernen, ist es völlig legitim, jede
Komponente selbst zu schreiben. Wenn Sie allerdings schnell Resultate sehen
möchten oder die Webseite professionell nutzen möchten, sollten Sie lieber auf
bereits existente Programme zurückgreifen.
Bei fast allen Programmen gilt: Irgendjemand hat so etwas bereits vor Ihnen gemacht. Vermutlich hat er es besser gemacht. Eventuell existiert dieses Programm
bereits seit Jahren, hat mehrere Tausend Anwender, eine aktive Community und
hat bereits geschlossene Sicherheitslücken. In so einem Fall ist es durchaus angebracht, bereits existierende Lösungen zu nutzen und, sofern es die Lizenz erlaubt,
Ihren Bedürfnissen anzupassen.
Dieser Ratschlag gilt insbesondere bei sicherheitsrelevanten Komponenten: Versuchen Sie nicht, einen Verschlüsselungsalgorithmus selbst zu implementieren
(höchstens zu Übungszwecken). Greifen Sie auf ausgereifte, bereits existierende
Lösungen zurück.
6.5 Best Practices
Seite 37
6.5.10 Usereingaben validieren
Folgen Sie immer dem Grundsatz: Traue niemals den Eingaben eines Users. Gehen Sie immer vom Schlimmsten aus. Vertrauen Sie nicht darauf, dass der Nutzer
Ihr Programm so verwendet, wie Sie es möchten.
Zunächst muss ganz klar gesagt werden: Geben Sie nicht dem Nutzer die Schuld.
Bei einer Sicherheitslücke oder einem ungeplanten Systemabsturz ist die Schuld
zunächst beim Programmierer zu suchen. Natürlich gibt es Nutzer, die in böswilliger Absicht Ihr Programm zum Absturz bringen oder Daten stehlen wollen. Aber
auch hier muss es zunächst eine Sicherheitslücke existieren, die ein Programmierer
verursacht hat.
Deshalb: Sobald der Nutzer Einfluss auf Ihr Programm nehmen kann – sei es
durch direkte Eingaben oder durch das Verändern einer Datei, aus die Sie lesend
zugreifen – müssen die Daten validiert werden. Validierung bedeutet, zu prüfen,
ob die Daten gültig sind.
Sind die Daten nicht valide, bleibt Ihnen überlassen, wie Sie damit umgehen. Bei
manchen Fehlern kann es durchaus legitim sein, das Programm zu beenden. Von
anderen Fehlern kann sich erholt werden, sodass das Programm weiterlaufen
kann.
Man muss sich jedoch eingestehen, dass es das perfekte Programm nur in der
Theorie gibt. Man kann leider nicht an alle Eventualitäten und Sonderfälle denken,
sodass Sicherheitslücken nie ausgeschlossen sind. Aber Sie können es eventuellen Angreifern schwer machen – indem Sie zumindest die offensichtlichen und
einfachen Angriffsszenarien berücksichtigen und verhindern.
Beispiel 6.11: Bankkonto Teil 1
In Studienbrief 5 mussten Sie ein Bankkonto implementieren, auf dem Geld
eingezahlt und abgeholt werden konnte. Betrachten wir im folgenden nochmal ein vereinfachtes Beispiel. Der Geldbetrag wird nur in Euro angegeben,
und der Dispo spiegelt den minimalen, negativen Betrag wieder, der auf
dem Konto sein darf:
public class BankAccount()
{
private int euro;
private int dispo;
private static final int DEFAULT_DISPO = -500;
public BankAccount(euro, dispo)
{
this.euro = euro;
this.dispo = dispo;
}
public BankAccount(euro)
{
this(euro, DEFAULT_DISPO);
}
B
Seite 38
Studienbrief 6 Coding Conventions und Best Practices
public void deposit(int euro)
{
this.euro += euro;
}
public void draw(int euro)
{
this.euro -= euro;
}
}
Der Code ist, was die Eingaben der Methoden angeht, viel zu gutgläubig.
Was würde passieren, wenn jemand bei der Methode deposit() einen negativen Betrag angibt? Das Konto des Empfängers würde dann um den
Geldbetrag verringert werden. Beim Abheben haben wir auch nicht auf den
Dispo geachtet!
B
Beispiel 6.12: Bankkonto Teil 2
Wir müssen also gewisse Abfragen in den Code einbauen. Zudem müssen
wir uns entscheiden, was passiert, wenn eine falsche Eingabe vorgenommen wird. Wir entscheiden uns hier dafür, den Erfolg oder Misserfolg der
Transaktion als boolean-Wert zurückzugeben:
public void deposit(int euro)
{
if(euro < 0)
{
return false;
}
this.euro += euro;
return true;
}
public void draw(int euro)
{
if(euro < 0)
{
return false;
}
if(this.euro - euro < this.dispo)
{
return false;
}
this.euro -= euro;
return true;
}
Jetzt haben wir darauf geachtet, dass keine negativen Beträge übergeben
6.5 Best Practices
Seite 39
werden können. Zudem haben wir den Dispo beim Geldabheben mit einbezogen. Aber hier enden die möglichen Falscheingaben noch nicht.
Beispiel 6.13: Bankkonto Teil 3
B
Wir wir in Studienbrief 1 gelernt haben, wird für eine Variable ein gewisser
Speicherplatz reserviert. Bei einer int-Variable sind dies 32 Bit. Eine solche
Variable hat also einen Maximal- und einen Minimalwert. Wenn wir über
den Maximalwert hinaus addieren, kommt es zum zu genannten Overflow.
Das oberste Bit wird auf 1 gesetzt, der Geldbetrag wird negativ.
Probieren wir dies einmal aus. Angenommen, wir haben ein Konto, auf dem
sich 500 Euro befinden:
account.deposit(Integer.MAX_VALUE);
Hier kommt es garantiert zum Overflow, da MAX_VALUE die maximal darstellbare positive Zahl ist, die in einer int-Variable gespeichert werden kann.
Wir müssen also darauf achten, dass so etwas nicht passieren kann:
public void deposit(int euro)
{
if(euro < 0)
{
return false;
}
if(this.euro + euro < euro)
{
return false;
}
this.euro += euro;
return true;
}
Mit dieser einfachen Abfrage haben wir einen Overflow verhindert. Wenn
wir etwas einzahlen, darf der neue Betrag nicht kleiner sein als der ursprüngliche Betrag.
Beispiel 6.14: Bankkonto Teil 4
Aber was ist mit dem umgekehrten Fall? Was, wenn wir soviel Geld abheben,
dass wir einen positiven Betrag auf dem Konto haben? Hier sprechen wir
von einem Underflow. Und so verhindern wir ihn:
public void draw(int euro)
{
if(euro < 0)
B
Seite 40
Studienbrief 6 Coding Conventions und Best Practices
{
return false;
}
if(this.euro - euro < this.dispo)
{
return false;
}
if(this.euro - euro > euro)
{
return false;
}
this.euro -= euro;
return true;
}
6.6 Zusammenfassung
In diesem Studienbrief sind wir ausführlich auf Coding Conventions und einige
Best Practices für Java eingegangen. Zunächst haben wir wiederholt, wie Klassen,
Methoden und Variablen in Java zu benennen sind. Im Anschluss wurde darauf eingegangen, wie lang beispielsweise eine Codezeile sein sollte und wie der
Zeilenumbruch am besten umzusetzen ist.
Um Ihr Programm zu dokumentieren, sollten Sie bevorzugt Javadoc einsetzen.
Durch die einfach zu erlernende Notation und dem gleichnamigen Tool können
Sie aus Ihrer Dokumentation ein interaktives HTML-Dokument erzeugen, wie Sie es
bereits von der Java-API kennen. Über Tags können Sie beispielsweise Parameter
näher dokumentieren oder auf andere Methoden und Klassen verweisen.
Bei den Best Practices haben wir eine Vielzahl von Empfehlungen betrachtet, durch
deren Beherzigung Sie besseren Code verfassen können. Wir mussten uns an dieser
Stelle aber auch eingestehen, dass es nicht den perfekten Code gibt. Das sollte Sie
aber nicht davon abhalten, Ihren Code so gut wie möglich zu verfassen.
Wenn wir diesen Studienbrief in einem Satz zusammenfassen müssten, würden
wir uns wiederum des verbreiteten Zitats bedienen:
Always code as if the person who ends up maintaining your code is a violent
psychopath who knows where you live.10
10
Genaue Herkunft nicht bekannt. Eventuell John F. Woods.
6.7 Übungen
Seite 41
6.7 Übungen
Den Lösungscode zu den Übungen 6.1 bis 6.4 finden Sie am Ende dieses Dokuments sowie im einfprog-Git-Repository. Falls Sie es im letzten Semester noch
nicht ausgecheckt haben, müssen Sie Folgendes im Terminal eingeben:
git clone ir99ijoj:einfprog
Anstelle von ir99ijoj müssen Sie den Hostnamen eintragen, den Sie für den
Gitserver gewählt haben. Wenn Sie sich an die Videos gehalten haben, sollte der
Hostname Ihrem Nutzernamen entsprechen.
Die Übungen 6.1 bis 6.5 sind optional. Nutzen Sie diese Übungen, um Ihr Wissen
wieder aufzufrischen und routinierter beim Programmieren zu werden.
Die Übung 6.6 ist nicht optional.
Übung 6.1: FizzBuzz
Ü
Eine äußerst beliebte Testfrage bei Bewerbungsgesprächen ist der so genannte FizzBuzz-Test. Die Aufgabenstellung ist dabei Folgende:
Schreiben Sie ein Programm, das die Zahlen von 1 bis 100 ausgibt. Wenn
die Zahl durch 3 teilbar ist, geben Sie statt der Zahl „Fizz“ aus. Wenn die
Zahl durch 5 teilbar ist, geben Sie statt der Zahl „Buzz“ aus. Wenn die Zahl
durch 3 und durch 5 teilbar ist, geben Sie „FizzBuzz“ aus.
Lösen Sie gerade gestellte Aufgabe. Geben Sie dabei jede Zahl oder Zeichenkette in einer eigenen Zeile aus.
Übung 6.2: The Programmer’s Drinking Song
Der „Programmer’s Drinking Song“ hat folgenden Text:
99 little bugs in the code, 99 bugs in the code, Fix one bug, compile it again, 100 little bugs in the code. (go to start if bugs>0)
Implementieren Sie ein Programm, das den Song so lange singt, bis die
Anzahl der Bugs < 0 ist.
10 https://jazzy.id.au/2010/09/20/cracking_random_number_generators_part_1.html
Ü
Seite 42
Ü
Studienbrief 6 Coding Conventions und Best Practices
Übung 6.3: Pferderennen
Implementieren Sie ein Pferderennen, bei denen die Pferde nacheinander
eine zufällige Anzahl an Schritten (zwischen -2 und 6) vor oder zurück
gehen.
Die Rennstrecke soll dabei eine gewisse Länge und eine gewisse Anzahl an
Pferden haben. Die Länge der Strecke wird beim Anlegen übergeben. Die
Pferde können extra angelegt und über eine Methode addHorse übergeben
werden.
Die Methode start startet das Rennen. In der Methode wird auf jedem
Pferd in einer Schleife die Methode start aufgerufen. Dies geschieht so
lange, bis ein Pferd die Distanz zurückgelegt hat.
Beim Aufruf jeder run-Methode gibt das Pferd in dem Terminal aus, was es
gerade tut. Denken Sie sich einige Sätze dazu aus.
Mit einer 30%igen Chance bewegt es sich gar nicht, sondern frisst Gras!
Jedes Pferd hat natürlich einen Namen, damit man Wetten abschließen kann.
Die Wetten implementieren wir an dieser Stelle nicht.
Achten Sie darauf, dass die Pferde aus Boxen starten und dadurch nicht
beliebig rückwärts gehen können.
Angenommen, wir haben eine Rennstrecke der Länge 8 und 3 teilnehmende
Pferde. Folgende Beispielausgabe könnte im Terminal erscheinen:
Diamond Star takes 3 steps forward. Go, Diamond Star!
Heisses Eisen eats gras.
Pacific Popsicle tries to step backwards. But there is the box!
Diamond Star takes 4 steps forward. Go, Diamond Star!
Heisses Eisen takes 6 steps forward. Go, Heisses Eisen!
Pacific Popsicle eats gras.
Diamond Star takes 2 steps backwards! It seems confused.
Heisses Eisen takes 3 steps forward. Go, Heisses Eisen!
Heisses Eisen wins the race!
Ü
Übung 6.4: Fibonacci
Die Fibonacci-Folge ist eine unendliche Reihe von Zahlen. Die nächste Zahl
in der Reihe wird aus der Summe ihrer beiden Vorgänger berechnet.Die
Folge beginnt mit den Zahlen 1,1.
Hier ein kleiner Ausschnitt: 1, 1, 2, 3, 5, 8, 13, 21, 34
Schreiben Sie ein Programm, das die Fibonacci-Reihe bis zum n-ten Element
ausgibt. Das n wird der jeweiligen Methode als Parameter übergeben.
6.7 Übungen
Übung 6.5: Code verstehen
Seite 43
Ü
Was tut folgender Code?
// a and b are integers
a = a - b;
b = b + a;
a = b - a;
Übung 6.6: Codeverständnis, Dokumentation und Testing (25 Punkte)
Deadline für Bonuspunkte: 05. Juni 2015
Spätestens jetzt müssen Sie das Reposity einfprog auschecken. Eine Anleitung dazu finden Sie auf Seite 41. In diesem Repository finden Sie unter
SB6/Aufgabe_6 eine .java-Datei.
Versuchen Sie, den Code zu verstehen. Ihre Aufgaben sind die Folgenden:
• Dokumentieren Sie den Code
• Führen Sie ein Refactoring durch
• Schreiben Sie für den Code im Anschluss Tests (JUnit)
Laden Sie das Ergebnis in Ihr Repository unter SB6/Aufgabe_6/ hoch. Schreiben Sie im Anschluss eine Email an [email protected]. Ihr Code wird
manuell bewertet.
Ü
6.7 Übungen
Verzeichnisse
Seite 45
Liste der Lösungen zu den Kontrollaufgaben
Liste der Lösungen zu den Kontrollaufgaben
Lösung zu Kontrollaufgabe 6.1 auf Seite 17
run -- Methode
isValid -- Methode
SPEED_OF_LIGHT -- Konstante
Browser -- Klasse (o. Interface)
file -- Variable
Deletable -- Interface
deleteLast -- Methode
firstValue -- Variable
Lösung zu Kontrollaufgabe 6.3 auf Seite 24
Das Zweierkomplement wird folgendermaßen gebildet: Alle Bits negieren, dann
plus Eins rechnen.
Wenn die Zahl eine Zweierpotenz ist, steht nur an einer einzigen Stelle eine Eins,
sonst nur Nullen. Bei der Negation steht nun an der Stelle, wo vorher die Eins
stand, eine Null. Sonst besteht die negierte Zahl nur aus Einsen. Wenn Sie nun
plus Eins rechnen, steht an der Stelle, an der im Original eine Eins stand, wieder
eine Eins. Durch den bitweisen Und-Operator kommt nun wieder die OriginalZahl
heraus.
Wenn die Zahl keine Zweierpotenz ist, entsteht bei der Verundung nicht die Originalzahl. Probieren Sie dies einfach an einigen Beispielen aus.
Lösung zu Kontrollaufgabe 6.5 auf Seite 34
Die Länge und der Inhalt des Strings sind final. Nach dem Aufruf des Konstruktors können die Werte nicht mehr verändert werden.
Seite 47
Verzeichnisse
Seite 49
Verzeichnisse
I. Beispiele
Beispiel
Beispiel
Beispiel
Beispiel
Beispiel
Beispiel
Beispiel
Beispiel
Beispiel
Beispiel
Beispiel
Beispiel
Beispiel
Beispiel
6.1:
6.2:
6.3:
6.4:
6.5:
6.6:
6.7:
6.8:
6.9:
6.10:
6.11:
6.12:
6.13:
6.14:
Namenskonventionen bei Interfaces . . . . . .
Interface als Suffix . . . . . . . . . . . . . . . .
Namenskonventionen bei Methoden . . . . . .
Namenskonvention bei Konstanten . . . . . . .
Zeilenumbruch bei Nicht-Zuweisungsoperatoren
Zeilenumbruch bei Zuweisungsoperatoren . . .
Zweierpotenz . . . . . . . . . . . . . . . . . . .
Dokumentation der Java-API . . . . . . . . . . .
DRY: Matrixoperationen . . . . . . . . . . . . .
One method, one thing: Matrixoperationen . . .
Bankkonto Teil 1 . . . . . . . . . . . . . . . . .
Bankkonto Teil 2 . . . . . . . . . . . . . . . . .
Bankkonto Teil 3 . . . . . . . . . . . . . . . . .
Bankkonto Teil 4 . . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
13
13
14
16
19
20
23
27
29
30
37
38
39
39
. . . . . .
. . . . . .
aus Passig
. . . . . .
. . . . . .
. . . . .
. . . . .
[2013])
. . . . .
. . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
18
21
21
23
35
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
17
21
24
32
34
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
26
II. Exkurse
Exkurs
Exkurs
Exkurs
Exkurs
Exkurs
6.1:
6.2:
6.3:
6.4:
6.5:
Einrückung in Python . . . . . . . . . . .
Was jeder über Encoding wissen sollte . .
T_PAAMAYIM_NEKUDOTAYIM (entnommen
Tasks in Eclipse . . . . . . . . . . . . . .
SecureRandom und Random . . . . . . . .
III. Kontrollaufgaben
Kontrollaufgabe
Kontrollaufgabe
Kontrollaufgabe
Kontrollaufgabe
Kontrollaufgabe
6.1:
6.2:
6.3:
6.4:
6.5:
Syntaxbausteine erkennen
Encoding . . . . . . . . . .
Zweierpotenz . . . . . . .
MatrixOperation: Multiply .
Immutable Strings . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
IV. Tabellen
Tabelle 6.1: Die wichtigsten Javadoc-Tags
V. Literatur
Google. Google java style, 2014. URL http://google-styleguide.googlecode.com/svn/trunk/javaguide.
html.
Martin Hitz, Gerti Kappel, Elisabeth Kapsammer, and Werner Retschitzegger. UML@Work. dpunkt Verlag,
2005.
Robert C. Martin. Clean Code: A Handbook of Agile Software Craftsmanship. Prentice Hall, 2008.
Oracle. Code conventions for the java programming language, 1999. URL http://www.oracle.com/
technetwork/java/codeconvtoc-136057.html.
Johannes Passig, Katrin Jander. Weniger schlecht programmieren. O’ Reilly, 2013.
Seite 50
Verzeichnisse
James Roper. Cracking random number generators - part 1, 2010. URL https://jazzy.id.au/2010/09/20/
cracking_random_number_generators_part_1.html.
Joel Spolsky. The absolute minimum every software developer absolutely, positively must know about unicode
and character sets (no excuses!), 2003. URL http://www.joelonsoftware.com/articles/Unicode.html.
Liste der Lösungen zu den Übungen
Liste der Lösungen zu den Übungen
Übungsaufgaben zu Studienbrief 6
Lösung zu Übung 6.1 auf Seite 41
public static void doFizzBuzz()
{
for(int i = 1; i<101; i++)
{
if(i % 15 == 0)
{
System.out.println("FizzBuzz");
}
else if(i % 5 == 0)
{
System.out.println("Buzz");
}
else if(i % 3 == 0)
{
System.out.println("Fizz");
}
else
{
System.out.println(i);
}
}
}
Lösung zu Übung 6.2 auf Seite 41
public static void doDrinkingSong()
{
int numberOfBugs = 99;
while (numberOfBugs > 0)
{
sing(numberOfBugs);
numberOfBugs++;
}
}
public static void sing(int numberOfBugs)
{
StringBuilder songBuilder = new StringBuilder();
songBuilder.append(String.format(
"%d little bugs in the code,\n",
numberOfBugs));
songBuilder.append(String.format(
"%d bugs in the code,\n",
numberOfBugs));
songBuilder.append(String.format(
"Fix one bug, compile it again,\n"));
Seite 51
Seite 52
Liste der Lösungen zu den Übungen
songBuilder.append(String.format(
"%d bugs in the code,\n",
numberOfBugs + 1));
System.out.println(songBuilder.toString());
}
Lösung zu Übung 6.3 auf Seite 42
Klasse HorseRace
import java.util.ArrayList;
public class HorseRace
{
private ArrayList<Horse> horses;
private int trackLength;
public HorseRace(int trackLength)
{
this.trackLength = trackLength;
horses = new ArrayList<Horse>();
}
public void addHorse(Horse horse)
{
horses.add(horse);
}
public void start()
{
boolean isFinished = false;
while(!(isFinished))
{
for(Horse horse : horses)
{
horse.run();
if(horse.passedGoal(trackLength))
{
isFinished = true;
String winningString =
String.format("%s wins the race",
horse.getName());
System.out.println(winningString);
break;
}
}
System.out.println();
}
}
public static void main(String[] args)
{
HorseRace race = new HorseRace(12);
Horse diamond = new Horse("Diamond Star");
Horse eisen = new Horse("Heisses Eisen");
Liste der Lösungen zu den Übungen
race.addHorse(diamond);
race.addHorse(eisen);
race.start();
}
}
Klasse Horse
import java.util.Random;
public class Horse
{
private String name;
private int position;
private
private
private
private
private
final
final
final
final
final
String
String
String
String
String
BOX_STRING;
EATING_STRING;
CONFUSED_STRING;
FORWARD_STRING;
STAND_STRING;
private static final int GRAS_EATING_PERCENT = 30;
private static final int MAX_STEP_VALUE = 6;
private static final int MIN_STEP_VALUE = -2;
public Horse(String name)
{
this.name = name;
this.position = 0;
BOX_STRING = String.format(
"%s tries to step backwards. But there is the box!",
this.name);
EATING_STRING = String.format(
"%s eats gras.",
this.name);
CONFUSED_STRING = String.format(
"%s takes STEPS steps backwards! It seems confused."
,
this.name);
FORWARD_STRING = String.format(
"%s takes STEPS steps forward. Go, %s!",
this.name,
this.name);
STAND_STRING = String.format(
"%s does not move. Did it forget how to run?",
this.name);
}
public String getName()
{
return this.name;
}
public boolean passedGoal(int trackLength)
Seite 53
Seite 54
Liste der Lösungen zu den Übungen
{
if (this.position >= trackLength)
{
return true;
}
else
{
return false;
}
}
public void run()
{
if(eatsGras())
{
System.out.println(EATING_STRING);
}
else
{
int steps = getSteps();
int newPosition = this.position + steps;
if(newPosition < 0)
{
System.out.println(BOX_STRING);
this.position = 0;
}
else if(steps < 0) // horse goes backwards
{
System.out.println(getConfusedString(steps));
this.position = newPosition;
}
else if(steps == 0)
{
System.out.println(STAND_STRING);
}
else
{
System.out.println(getForwardString(steps));
this.position = newPosition;
}
}
}
public boolean eatsGras()
{
Random random = new Random();
// value between 0 and 100
int randomValue = (int) (random.nextDouble() * 100);
if (randomValue <= GRAS_EATING_PERCENT)
{
return true; // horse eats gras
}
else
{
Liste der Lösungen zu den Übungen
return false;
}
}
private int getSteps()
{
Random random = new Random();
int stepSum = Math.abs(MIN_STEP_VALUE) + MAX_STEP_VALUE
;
// random number of steps
int randomValue = (int) (random.nextDouble() * stepSum)
;
// return random number of steps between minvalue und
maxvalue
return randomValue + MIN_STEP_VALUE;
}
public String getConfusedString(int steps)
{
return getStringWithReplacedSteps(CONFUSED_STRING,
steps);
}
public String getForwardString(int steps)
{
return getStringWithReplacedSteps(FORWARD_STRING, steps
);
}
/**
* Replaces the String "STEPS" within a given string with
the
the
number of steps.
*
/
**
private String getStringWithReplacedSteps(String
oldString, int steps)
{
return oldString.replace("STEPS", Integer.toString(Math.
abs(steps)));
}
}
Lösung zu Übung 6.4 auf Seite 42
public static void doFibonacci(int numberOfEntries)
{
int firstValue = 0;
int secondValue = 1;
System.out.println(secondValue);
for(int i = 1; i < numberOfEntries && i > 0 ; i++)
{
int newValue = firstValue + secondValue;
Seite 55
Seite 56
Liste der Lösungen zu den Übungen
System.out.println(newValue);
firstValue = secondValue;
secondValue = newValue;
}
}
Lösung zu Übung 6.5 auf Seite 43
Die Werte in a und b werden vertauscht.
Anhang
Anhang
Seite 57
Herunterladen