Wie kommt man zu Klassen in Java ?
„Wie kommt man zu Klassen? “
Frage eines Kursteilnehmers (2014-12)
(Die Frage ist so zu verstehen, daß gefragt wird, wie man ein Programm mit mehreren Klassendeklarationen schreibt, und wie man dabei entscheidet, wie viele Klassendeklarationen dies sein sollen und wie sie deklariert werden sollen.)
Jeder Objekttyp wird durch eine Klasse festgelegt, entweder eine Klasse aus einer Bibliothek oder einer Klasse aus dem Programm. Man kommt zu Klassendeklarationen, wenn man Objekte eines bestimmten Typs wünscht und es noch keine dafür passenden Klasse in der Standardbibliothek oder anderen vertrauenswürdigen Bibliotheken gibt.
(Das Programm enthält dann mehr als eine Klassendeklaration, wenn man dann mehr als eine solche Klassendeklaration benötigt und schreibt.)
- I prefer another approach when I do not have experience with a type of program: I start to write the program "procedurally".
- One then sees that certain combinations of operations repeat, so these combinations become functions.
- Next, one sees that some functions refer to a common state, so these functions then become methods of an object with this state. (Or I follow other patterns that suggest when to write a class, for example, as described in the book "Applying UML And Patterns" by Craig Larman.)
Es geht um objektorientierte Programmierung, nicht um klassenorientierte Programmierung!
Besser fragen: Wie kommt man zu Objekten?
Java will ja die „objektorientierte Programmierung“ unterstützen und nicht die „klassenorientierte Programmierung“. Die Klassendeklarationen sind in einigen Sprache nur Hilfsmittel, um Objekte zu erzeugen, andere objektorientierte Sprachen kommen ganz ohne Klassendeklarationen aus!
In einem Programm spielen vielleicht zwei verschiedene Objekte mit ein und derselben Klasse eine wichtige Rolle. Wenn man nur an Klassendeklarationen denkt, kann man so etwas gar nicht richtig verstehen.
Objekte sind spezialisierte simulierte Mini-Maschinen, man legt also ein Objekt an, wenn man solch eine Maschine braucht. Wenn man mehrere Objekte braucht, dann verwendet man für sie eine vordefinierte Standardklasse, falls es eine passende gibt. Andernfalls schreibt man nur eine einzige Klassendeklaration für die Objekte, wenn alle Objekte gleichartig sind (gleichartige Felder und Methoden). Nur, wenn die Objekte unterschiedlichen Typ haben sollen (unterschiedliche Felder und Methoden) muß man mehrere Klassendeklarationen schreiben.
Man kommt also zu Klassendeklarationen, wenn man Objekte unterschiedlichen Typs benötigt. Jeder Objekttyp wird dann durch eine Klassendeklaration festgelegt.
Von Prozeduren zu Objekten
Wenn man eine Prozedur aufruft, die mitzählen soll, wie oft sie aufgerufen wurde, dann ist dies mit einer klassischen Prozedure mit einer lokalen Variablen schwierig, weil die Variable nach dem Ende der Ausführung der Aufrufs wieder verschwindet. Man braucht also etwas wie eine Prozedure, dessen Variablen über den Aufruf hinaus erhalten bleiben: dies ermöglicht eine Objekt, das die Prozedur und eine Variable enthält.
Wenn man eine Prozedur aufrufen will, um eine Variable auf „1“ zu setzen, und eine andere, um dieselbe Variable wieder auf „0“ zu setzen, braucht man zwei Prozeduren, die einen gemeinsamen Zustand teilen: dies ermöglicht eine Objekt, das die beiden Prozeduren und eine Variable enthält.
Man könnte zwar zunächst denken, daß man die Anforderungen auch mit einer globalen Variablen erfüllen könnte, doch zum einen verschlechtern viele globale Variablen die Wartbarkeit eines Programmes und zum anderen lassen bei der Ausführung eines Programmes keine neuen globalen Variablen anlegen, wie bei Objekten. – Das Anlegen neuer Objekte zur Laufzeit wird aber oft benötigt.
Die Klassen sind nur Hilfmittel, um dann zur Laufzeit die benötigten Objekte anlegen zu können.
Gestaltungsfreiheit
Es ist nicht immer eindeutig, wie ein Programm auf mehrere Klassendeklarationen aufgeteilt wird, hierzu gehört auch eine gewisse Gestaltungsfreiheit.
Es gibt nicht immer eine eindeutige Lösung, sondern oft verschiedene Möglichkeiten.
(Bei den später erwähnte Entwurfsmuster und Entwurfsregeln, geht es auch weniger darum, sie alle zu befolgen, sondern eher darum, sie zu kennen, und dann zu wissen, wann es besser ist, sie zu befolgen, und wann nicht.)
Heute braucht man weniger Klassendeklarationen als früher
Gute objektorientierte Programmierung heißt nicht, möglichst viel Klassendeklarationen zu schreiben.
Heute kann man oft Methodenliterale oder -referenzen einsetzen, wo man früher Klassen schreiben mußte. Dann ist es manchmal besser, diese Methodenliterale oder -referenzen zu verwenden als Klassen.
Ältere Lehrbücher früherer Jahrzehnte geben diesen Aspekt noch nicht wieder. Daher ist es nicht verwunderlich, wenn sie mehr Klassendeklarationen in Programmen enthalten.
Wiederverwendung
Falls eine vordefinierte Standardklasse, wie »java.lang.String«, verwendet werden kann, dann keine neue Klassendeklaration schreiben, sondern die Standardklasse für die Objekte verwenden!
(Ausnahme: Man will Standardklassen zu Übungszwecken nachprogrammieren.)
Lernen aus Quellen
In diesem Kurs wurden schon einige Klassen aus der Standardbibliothek vorgestellt, die als Beispiele dafür dienen können, wie Klassen gestaltet und wie Aufgaben auf mehrere Klassen aufgeteilt werden können.
Weiterlesen! In weiteren Lektionen dieses Kurses hier, wird noch zu der Frage „Wie kommt man zu Klassen?“ Hilfreiches behandelt.
Viele Übungen und Projekte bearbeiten
in Programmierforen lesen/schreiben
Machen Sie sich bewußt, daß Sie schon viele Beispiele von Klassen aus der Standardbibliothek kennen, wie beispielsweise »java.lang.String«, … und orientieren Sie sich beim Schreiben von Klassendeklarationen an diesen Vorbildern
Orientieren Sie sich auch an den Programmbeispielen aus diesem Kurs und aus anderen Quellen
Hilfreich auf diesem Gebiet ist Erfahrung, die man durch das Lösen von Programmieraufgaben gewinnen kann, sowie Lektüre der Literatur zu objektorientiertem Design und Softwarearchitektur
Orientieren an Beispielen: Standardklassen, Lehrbücher, Java-Programme, Quellcode der Standardbibliothek und -demos, Software-Entwurfsmuster aus Literatur (siehe unten, sowie Web)
Teilnahme an Treffen von Java-Benutzergruppen
„Eine Klassendeklaration schreiben“
Wichtig: Wenn eine Aufgabe verlangt, „eine Klassendeklaration zu schreiben“, dann wird damit NICHT ausgeschlossen, daß man zu dieser Klassendeklaration auch noch weitere Hilfsklassendeklarationen schreibt, oft ist diese sogar notwendig oder hilfreich. Es wird auch nicht ausgeschlossen, daß die Klassendeklaration nur klein ist und den größten Teil der Arbeit von einer anderen Klasse (Standardklasse) erledigen läßt, die es schon vorher gab (composition with library classes).
(Dies gilt auch für den Fall, daß man „eine Methodendeklaration schreiben“ soll.)
Dies gilt jedenfalls in diesem Kurs oder bei selbstgestellten Aufgaben. Sonst muß man (beispielsweise in einer Prüfung) gegebenenfalls nachfragen, ob weitere Hilfsklassen ausgeschlossen sind.
Klassen über ihre Felder finden
Falls eine einzelne lokale Blockvariable mit elementaren Typ ausreicht, dann kann man diese verwenden, ohne Objekte einführen zu müssen. Es sollte immer die einfachste mögliche Lösung bevorzugt werden!
Objekte fassen oft mehrere zusammengehörige Felder zusammen oder werden verwendet, wenn eine Variable länger existieren soll, als der Block, in dem sie erzeugt wurd
- Ein Objekt, das für seine Arbeit andere Objekte braucht, erhält unter Umständen Felder mit Referenzen auf diese anderen Objekte
- Wenn bestimmte Informationen zusammengehören (siehe unten), werden sie als Felder in einem Objekt zusammengefaßt, dieses enthält dann auch alle Methoden, die nur mit diesen Feldern zu tun haben
- Wenn bestimmte Informationen nicht zusammengehören, werden sie in verschiedenen Objekten aufbewahrt
Wenn Objekte mit benutzerdefinierten Klassen angelegt werden müssen, kann man zunächst erst einmal gedanklich für jedes Feld eine Klasse definieren.
Nun faßt man alle Felder in einer Klasse zusammen, die zusammengehören.
Nun fügt man Methoden, welche direkt mit diesen Feldern zu tun haben, zu der Klasse hinzu.
Es werden ggf auch noch Felder für Hilfsobjekte, welche von diesen Methoden benötigt werden zur Klasse hinzugefügt und rechtzzeitig initialisiert.
Wann gehören Felder zusammen?
Dies ist etwas vage, weil der Programmier hier tatsächlich eine gewisse Freiheit der Interpretation und Gestaltung besitzt!
Ein Indiz dafür ist, daß Felder zusammengehören ist:
- sie beschreiben gemeinsame eine reale oder vorgestellte Sache, der Name dieser Sache soll dann der Name der Klasse werden
- sie werden gemeinsam zur Erfüllung einer bestimmten Aufgaben benötigt
- sie werden gemeinsam an eine Methode übergeben
- sie werden gleichzeitig angelegt
- falls sie nicht mehr benötigt werden, dann alle zur selben Zeit
Wann werden Felder werden gemeinsam für eine bestimmte Aufgabe benötigt?
- zur Implementation einer Schnittstelle
- zur Übergabe an eine Methode
- zur Rückgabe aus einer Methode
- Wenn eine Aktivität alle diese Felder gemeinsam benötigt
Beispiel (Refaktor)
f( a, x );
f( a, y );
f( a, z );
--->
o = new C( a );
o.f( x );
o.f( y );
o.f( z );
Klassen ohne Felder durch Zusammengehörigkeit von Methoden finden
Es kann auch mal Klassen ohne Felder geben, die zusammengehörige Methoden zusammenfassen.
Klassen durch Zerlegen finden
Bei Klassen mit sehr vielen Feldern und Methoden sollte geprüft werden, ob diese sinnvoll in mehrere kleine Klassen zerlegt werden können
Falls eine Klasse zu groß und dadurch inübersichtlich eird, versuchen in mehrere Klassen zu zerlegen,
Klassen möglichst so universell schreiben, daß sie in anderen Programmen wiederverwendet werden können
universelle Klassen wandern in Bibiliothek
Wenn eine Klasse etwas Universelles und etwas Spezielles enthält, dann möglichst in eine Klasse für das Universelle und eine für das Spezielle zerlegen.
Statische und Nicht-statische Methoden
Manchmal finden sich statische und nicht statische Einträge (Methoden und Felder) gemeinsam in einer Klasse, obwohl sie in manchen Einzelfällen wenig miteinander zu tun haben. Dann wird es manchmal aus übersichtlicher empfunden, solch eine Klasse in zwei Klassen aufzuteilen, deren eine nur die statischen Methoden und deren andere nur die nicht-statischen Methoden enthält.
Klassen als Erweiterung einer einzelnen Methoden
Wenn eine einzelne Methode so erweitert werden soll, daß sie sich bestimmte Informationen über mehrere Aufrufe hinweg merkt, dann kann sie in eine Klasse gepackt werden, deren Objekte im wesentlichen diese Methode repräsentieren (und die benötigten Felder enthalten). Geschichtlich kann man die Entstehung von Klassen in Simula so begreifen.
Klassen durch Anwendung von Software-Entwurfsmuster finden
Siehe Literatur (unten)!
Klassen durch Anwendung von Software-Entwurfsregeln finden
Lesen Sie einige der wichtigsten Regeln für den Entwurf von Software nach:
- DRY - Don't repeat yourself!
- KISS - Keep it simple, stupid!
- Open/Closed principle (Uncle Bob, 1996)
- High Cohesion
- Low Coupling
- Protected Variations (Alistair Cockburn, 1996), Encapsulate Change
- Don't talk to strangers! (Demeter's Law)
- Tell, don't ask!
- Design by contract (Bertand Meyer, 1988)
- Single Responsibility Principle
- Principle of Least Astonishment/Surprise
- God class (God object)
- Dependency Inversion Principle
- Information Hiding (David Parnas, 1972)
- Liskov Substitution Principle (Barbara Liskov)
- Test Driven Development
- Composition Over Inheritance
Treiber identifizeren
Das verallgemeinerter Treiberprinzip (encapsulate change, Protected Variations), rät dazu alles in eine Klasse (oder eine Gruppe von Klassen) auszulagern, was zu ein bestimmten Gerät oder einer Sprache oder einer Schnittstelle gehört, die sich einmal ändern könnte, beispielsweise: Drucker, GUI, natürliche Sprache (Deutsch/Englisch), formale Sprache (HTML, XHTML). Dies kann bei einer „GUI“ auch einmal das „Hauptprogramm“ sein.
Klassen durch monolithisch-prozedurale Programmierung und Refaktoren/Smells finden
Es soll eine Fähigkeit zu einem Programm hinzugefügt werden. Zuerst mit Aufruf vorhandener Methode probieren, falls das nicht geht, Methodendeklaration (einschließlich Hilfsmethodendeklarationen) probieren. Falls das nicht geht, oder zu unübersichtlich, probieren, ob Klassendeklaration hilft.
Eine andere Vorgehensweise, ist es, zunächst erst einmal das ganze Programm in eine Klasse und eine Methode schreiben. Wenn das Programm dann größer wird und der Autor schon einige Erfahrungen durch Lesen von Lehrbüchern und Programmen gesammelt hat, wird ein Punkt kommen, an dem es sich förmlich aufdrängt, bestimmte Teile aus diesem großen Block in eine separate Methode oder Klasse auszulagern, was dann durch Refaktorieren erreicht wird.
Man kann eine große Klassendeklaration durch Zerlegen in mehrere kleinere Klassendeklarationen oft übersichtlicher machen, aber hierfür ist es wichtig, wie genau die Zerlegung durchgeführt wird! Dies wird in der Literatur zu Entwurfsmustern und Refaktoren erklärt.
Allerdings muß man hierzu eine Buch über Refactoring oder Smells gelesen haben.
Klassen durch reale Objekte finden
Aufgabenstellung Eine Methode soll Züge auf einem Schachbrett akzeptieren bis sie ein Schachmatt erkennt. Falls eine nicht-erlaubter Zug eingegeben wird, soll das Programm eine Fehlermeldung ausgeben und erneut zur Eingabe desselben Zugs auffordern.
Mögliche Gestaltung Bei einem realen Schachbrett gibt es als Objekte die Figuren und das Schachbrett selber. Diese realen Objekte können als Anregung für Objekte in dem Programm verwendet werden. Dabei könnten alle Figuren durch eine Klasse dargestellt werden. Es wäre aber auch möglich, für jeden Typ von Figur eine extra Klasse zu definieren. Bei einigen Klassen, die man auf diese Weise gewinnt, könnte es sich zeigen, daß sie im Programm gar nicht benötigt werden. Umgekehrt, kann es auch günstig sein, in einem Programm Objekte zu verwenden, zu denen es keine reale Entsprechung gibt.
Anmerkungen
Die Frage „Wie kommt man zu Klassen? “ ist gut! Aber warum eigentlich wurde nicht schon gefragt, wie man zu Methodendeklarationen kommt, als die eingeführt wurden? Dies nämlich mindestens ebenso wichtig und sollte vorher verstanden worden sein, da Klassendeklarationen auf Methodendeklarationen aufbauen! Daher wurden Methoden im Grundkurs auch so ausführlich behandelt! Bestimmte Methodenrefaktoren führen direkt zu Klassen! (Refaktoren wurden auch schon im Grundkurs behandelt!) Beispielsweise kann das Aufteilen einer Methode auf zwei kleinere Methode bedeuten, daß diese beiden kleineren Methoden nun besser gemeinsam in eine neue Klasse gepackt werden, welche dann auch einige der bisherigen Blockvariablen der Methode (die von den beiden neuen Methoden, und nur von diesen, geteilt werden sollen) als Felder aufnimmt. Um zu verstehen, warum es günstig ist, diese Einträge alle in eine separate Klasse auszulagern, muß man aber auch das Prinzip des minimalen Gültigkeitsbereichs kennen, das ebenfalls schon im Grundkurs vermittelt wird.
Anfänger neigen dazu, alles in eine große Methode zu schreiben anstatt Code sinnvoll auf mehrere Methoden zu verteilen. Dadurch sind ihnen viele elegante Lösungswege verbaut.
(Siehe das Programm „Rekursives Durchstreifen“)
Literatur
Zunächst
- Applying UML and Patterns, by Craig Larman
- E. Gamma, R. Helm, R. Johnson, and J. Vlissides. Design Patterns: Elements of Reusable ObjectOriented Software. Addison-Wesley, Reading, Massachusetts, 1995.
- Refactoring: Improving the Design of Existing Code; by Martin Fowler, Kent Beck, John Brant, William Opdyke, Don Roberts
Ferner
- J. Rumbaugh, M. Blaha, W. Premerlani, F. Eddy, and W. Lorensen. Object-Oriented Modeling and Design. Prentice-Hall, Englewood Cliffs, New Jersey, 1991.
- B. Meyer. Object-oriented software construction. Prentice-Hall, Englewood Cliffs, New Jersey, 1988.
- Booch, Grady, Object-Oriented Analysis and Design with Applications (2nd ed.), Benjamin/ Cummings, Redwood City, CA, 1994.
- I. Jacobson and et al. Object-Oriented Software Engineering - A Use-case Driven Approach. Addison-Wesley, 1992.
- Writing Effective Use Cases, by Alistair Cockburn Use Case Driven Object-Modeling with UML, Doug Rosenberg and Kendall Scott
- The Early History Of Smalltalk by Alan Kay
- Patterns of Enterprise Application Architecture by Fowler
Ferner
- Refactoring Workbook by William Wake
- Object-Oriented Design Heuristics by Arthur Riel
- Rebecca Wirfs-Brock, Brian Wilkerson, Lauren Wiener. Designing Object Oriented Software. Prentice Hall, 1990.
- Fundamentals of Object-Oriented Design in UML, Meillir page-jones
- Abstraction and Specification in Program Development by Barbara Liskov
- Program Development in Java: Abstraction, Specification, and Object-Oriented Design by B. Liskov, John Guttag
Software-Architektur und -Arbeit,
http://www.objectmentor.com/resources/articles/ocp.pdf
http://www.cs.cmu.edu/afs/cs/project/vit/ftp/pdf/intro_softarch.pdf
http://martinfowler.com/ieeeSoftware/whoNeedsArchitect.pdf
http://www.laputan.org/mud/
Structured Systems Analysis (DeMarco)
Object Oriented Software Engineering by Ivar Jacobson
Test-Driven Development by Kent Beck
The Pragmatic Programmer by Andrew Hunt
Domain Driven Design by Eric Evans
Working Effectively with Legacy Code by Michael Feathers
A Theory of Object-Oriented Design: The building-blocks of OOD and notations for representing them (with focus on design patterns.)
Martin Fowler. Analysis Patterns: Reusable Object Models. Addison-Wesley, 1997.
Brett McLaughlin, Gary Pollice, David West. Head First Object-Oriented Analysis and Design. O'Reilly, 2006.
An Object-Oriented Design Taxonomy, http://www.jonathanholloway.co.uk/thesis/MPhilThesis.pdf
Fundamentals of object oriented design and UML (Meilir Page-Jones)
Patterns oriented software architecture (Douglas Schmidt)
Software-Projekte
The Mythical Man-Month by Frederick Brooks
Code Complete, 2nd Ed. by Steve McConnell
Agile Principles, Patterns and Practices in C# (Robert C. Martin)
Siehe auch
- BARBARA LISKOV, STEPHEN ZILLES : Programming with abstract data types. In: ACM SIGPLAN Notices. (1974), Volume 9, Issue 4:50-59.
http://www.cs.iastate.edu/courses/archive/f06/cs362/papers/p50-liskov.pdf - BERTRAND MEYER : Applying "Design by Contract". In: Computer (IEEE). (1992), Volume 25, Issue 10:50-51.
http://www.cs.iastate.edu/courses/archive/f06/cs362/papers/meyer92.pdf
http://foundationsof.com/FoundationsOfProgramming.pdf
Ein früher Artikel zur objektorientierten Programmierung:
- Hierarchical Program Structures; Ole-Johan Dahl, C. A. R. Hoare; in Dahl, Dijkstra and Hoare, Structured Programming, Academic Press, London and New York, pp. 175-220, 1972.
Unter den »Klassikern« der objekt-orientierten Programmierung auf der Seite
http://e7l3.org/ref/classics.html
findet sich auch ein Text, an dessen Zustandkommen Stefan Ram etwas beteiligt war:
- Dr. Alan Kay on the Meaning of Object-Oriented Programming, E-Mail 2003, https://www.purl.org/stefan_ram/pub/doc_kay_oop_en