Beruflich Dokumente
Kultur Dokumente
4.1 Generizität
Einschränkungen in Java
Generizität ist sinnvoll, wenn mehrere Variablen vom gleichen, aber nicht von Anfang
an festgelegten Typ benötigt werden.
Faustregel: Generizität ist dort sinnvoll, wo mehrere Variablen vom gleichen (nicht
von Anfang an fix festgelegten) Typ notwendig sind.
Verwendbarkeit:
Laufzeiteffizienz:
Laufzeiteffizienz sollte nicht der primäre Faktor bei der Entscheidung zwischen
Generizität und Untertypbeziehungen sein.
Faustregel: Lass Überlegungen zur Laufzeiteffizienz beiseite.
Jede generische Klasse wird in genau eine Klasse mit JVM-Code übersetzt.
Typparameter werden durch die Schranke ersetzt, und Typparameter ohne Schranke
durch Object.
Der Abschnitt 4.3 aus Ihrem Text beschäftigt sich mit dynamischer Typinformation, speziell
mit Typabfragen (instanceof-Operator) und Typumwandlungen in Java. Hier sind die
wichtigsten Punkte und Erklärungen:
Jedes Objekt in Java hat eine Methode getClass(), die ein Objekt vom Typ Class
zurückgibt, welches die interne Repräsentation der Klasse des Objekts darstellt.
Es gibt ein Class-Objekt für jedes Interface, jede Klasse, jeden elementaren Typ sowie
davon abgeleitete Array-Typen.
Man kann Class-Objekte direkt ansprechen, z. B. int.class, Person.class, usw.
instanceof-Operator
Dynamische Typabfragen allein reichen nicht aus, um auf Methoden und Variablen
des dynamischen Typs zuzugreifen.
Explizite Typumwandlungen (Casts) werden verwendet, um den deklarierten Typ
einer Referenz zur Laufzeit zu ändern.
Faustregel
Diese Prinzipien dienen dazu, die dynamische Typinformation und Typumwandlungen in Java
effektiv und sicher zu nutzen.
Listenklasse und Interfaces aus Abschnitt 4.1.2 als Beispiel für homogene
Übersetzung.
Automatische Casts auf Object in Konstruktoren und Methoden, sinnlos und
optimierbar.
Nur sichere Formen der Typumwandlung verwenden, die keine Ausnahmen auslösen.
Bei dynamischen Typabfragen sicherstellen, dass Annahmen im alternativen Zweig
durch Zusicherungen abgesichert sind.
Verwendung von Generizität vor unsicheren Formen von Typumwandlungen.
Bei Generizität darauf achten, dass alle spitzen Klammern korrekt gesetzt sind.
Beispiel zeigt die Verwendung von generischen Klassen und Raw-Types in der
Methode equals.
Raw-Types entstehen durch homogene Übersetzung, Type-Erasure entfernt
Typparameter.
Wichtig: Spitze Klammern bei Typdeklarationen korrekt setzen, Generizität
bevorzugen.
Kovariante Eingangsparametertypen
Tiere (Animal), Futter (Food), Grass und Meat als Untertypen von Food.
Natürliche Erwartung: Cow isst Grass, Tiger isst Meat.
Schwierigkeit: Typsicherheit nicht direkt darstellbar.
Überladen von eat in Cow und Tiger mit spezifischeren Parametertypen (Grass und
Meat).
Reduziert die Notwendigkeit von dynamischen Typabfragen.
Faustregel
Binäre Methoden
Binäre Methoden haben mindestens einen formalen Parameter, dessen Typ gleich
der enthaltenden Klasse ist.
Beispiel: equal-Methode in Point2D und Point3D.
Verwendung einer abstrakten Oberklasse Point und uncheckedEqual-Methode für den
eigentlichen Vergleich.
Fazit
Dynamisches Binden in Java erfolgt über den dynamischen Typ eines Parameters.
Die auszuführende Methode wird durch den dynamischen Typ des Empfängers (z.B.,
x in x.equal(y)) festgelegt.
Multimethoden
Im Allgemeinen (nicht in Java) kann das dynamische Binden auch den dynamischen
Typ des Parameters einschließen.
Unterschied zwischen Überladen und Multimethoden.
Fehlerquellen in Java
Beispielumsetzung
Automatische Umwandlung
Annotationen in Java starten mit dem Zeichen "@", z.B., @Override vor
Methodendefinitionen.
Eigene Annotationen können mit Argumenten versehen werden und müssen vor
Verwendung deklariert werden.
Beispiel: @BugFix-Annotation mit Datenfeldern für Bug-Fix-Informationen.
Reflexion erlaubt den flexiblen Zugriff auf viele Details eines Programms zur Laufzeit.
Klassen, Methoden, Felder usw. können dynamisch analysiert werden.
Gefahr der Reflexion: Unkontrollierte Nutzung kann zu undurchschaubarem und
schwer wartbarem Code führen.
Zusammenfassung
Übliche Annotationen
@FunctionalInterface
Einführung in Java 8.
Verwendung: Kennzeichnet Interfaces mit genau einer abstrakten Methode.
Erlaubt Einsatz als Lambda-Ausdrücke.
Beispiel:
Reflexion
JavaBeans-Komponenten
@ConstructorProperties
Fazit
Betrachtungsebenen:
o Konzeptuelle Sichtweise: Hohe Ebene, Einbettung in das objektorientierte
Paradigma.
o Implementierungsebene: Niedrige Ebene, Eingriff in das Objektgefüge.
Ziel:
o Entwicklung eines Authentifizierungspakets für Bankensoftware.
o Überprüfung von Zugriffsrechten ohne direkte Änderungen an bestehendem
Code.
Herangehensweise:
o Identifikation kritischer Stellen (z. B., Methodenaufrufe, Datenbankzugriffe).
o Festlegung von Kriterien (Klassen, Namenskonventionen) zur Identifikation.
o Extraktion von Kontextinformationen (z. B., Zugriffsrechte, Daten).
Umsetzung:
o Parametrisierung durch Hinzufügen von zusätzlichem Code an identifizierten
Stellen.
o Abhängigkeit von Annahmen und Einhaltung von Projektregeln.
Aspektorientierte Terminologie
Separation-of-Concerns:
o Trennung von Belangen in der Programmierung.
o Core-Concerns: Kernfunktionalitäten, hoher Klassenzusammenhalt.
o Cross-Cutting-Concerns: Querschnittsfunktionalitäten, schwerer
Klassenzusammenhalt.
Parametrisierung:
o Lücken im Programm, die später befüllt werden.
o Abhängigkeit zwischen Lücken und Befüllung.
Einfluss der Annahmen:
o Identifikation von Programmstellen und Extraktion von Kontextinformationen.
o Wahrung von Einhaltung projektspezifischer Regeln und Konventionen.
4.6.2 AspectJ
Überblick:
o AspectJ ist ein Werkzeug für die aspektorientierte Programmierung in Java.
o Es arbeitet mit Java-Programmen, die aus mehreren Klassen bestehen.
o Zusätzliche Dateien mit der Erweiterung .aj erfassen gewünschte Änderungen
in der Java-Semantik.
o Der AspectJ-Compiler (ajc) wird für die Übersetzung verwendet, wobei Java-
Klassen, .aj-Dateien und die Bibliothek aspectjrt.jar gleichzeitig behandelt
werden.
Wichtige Konzepte:
o Join-Point: Identifizierbare Laufzeitpunkte im Programm, wie
Methodenaufrufe oder Objektzugriffe.
o Pointcut: Ein syntaktisches Element in einer .aj-Datei, das Join-Points
auswählt und kontextspezifische Informationen sammelt.
o Advice: Spezifiziert den auszuführenden Programmcode an einem Join-Point
(vor, nach oder rund um).
Pointcuts:
o Definieren eine Menge von Join-Points mit Syntax wie call(*
javax..*.add*Listener(EventListener+)).
o Operatoren wie ! (Negation), || (Vereinigung) und && (Schnittmenge) können
Pointcuts kombinieren.
Pointcut-Typen (Beispiele):
o call(MethodSignature), execution(ConstructorSignature), get(FieldSignature), usw.
o Sichtbarkeitsbasiert: within(Typepattern), withincode(Method/ConstructorSignature).
o Kontrollflussbasiert: cflow(Pointcut), cflowbelow(Pointcut).
Advices:
o Werden an bestimmten Join-Points ausgeführt.
o Typen: before(), after(), after() returning, after() throwing, around().
Beispiel-Advices:
java
before() : call(* Account.*(..)) { checkUser(); }
before(Connection connection) : connectionOperation(connection)
{ System.out.println("Operation auf " + connection); }
Aspects:
Beispiel-Aspect:
java
public aspect JoinPointTraceAspect {
private int callDepth = -1;
pointcut tracePoints(): !within(JoinPointTraceAspect);
before() : tracePoints() { callDepth++; print("Before", thisJoinPoint); }
after() : tracePoints() { callDepth--; print("After", thisJoinPoint); }
private void print(String prefix, Object message) { /* Implementierung */
}
}
Hintergrund:
Zusammenfassung:
5.1.2 Java-8-Streams
Java 8 Streams werden als spezielles Konzept in Java 8 betrachtet und als eine Form
von Iteratoren angesehen.
Externe Iteratoren in der objektorientierten Programmierung werden durch Streams
ersetzt, um den funktionalen Programmierstil zu unterstützen.
Lazy-Evaluation in Streams:
Entwickler können die Fähigkeiten von Streams durch das Erstellen neuer
Spliteratoren erweitern.
Spliteratoren sind eine erweiterte Form von Iteratoren, die parallele Verarbeitung
unterstützen.
Zeigt die Verwendung von Streams für die Faktorielle-Berechnung mithilfe von
LongStream.rangeClosed und reduce.
Grundlegende Erfahrungen:
Map-Reduce-Schema:
Weitere Erkenntnisse:
Variablenverwendung:
Denkweise:
Funktionsdesign:
Currying in Java
Vorgefertigte Lösungen
Thread-sichere Datenstrukturen
Vorgehensweise
Zusammenfassung
Objektorientierte Sicht
Zusammenfassung
Prozesserzeugung in Linux:
Unter Linux wird oft mit einer Shell in einem Terminal-Fenster gearbeitet.
Das Starten von Programmen erfolgt durch Eingabe des Programmnamens in die Shell.
Die Shell sucht nach der ausführbaren Datei im Verzeichnis und startet einen neuen
Prozess.
Kommandozeilenargumente:
Pipelines:
Hintergrundprozesse:
Beispiel: Shell-Skripte:
Schreiben von Shell-Skripten, die als Textdateien gespeichert und ausgeführt werden
können.
Nutzung von Variablen, Bedingungen (if ... else), Schleifen (for).
Verwendung von echo, test, if ... then ... else ... fi, for ... do ... done.
Zusammenfassung
Die main-Methode eines Java-Programms erhält ein Array von Zeichenketten als
Kommandozeilenargumente.
System.in ermöglicht die Eingabe von der Standardeingabe, während System.out und
System.err für die Standardausgabe und Fehlerausgabe stehen.
Dateiverarbeitung
Dateien werden über verschiedene Arten von Strömen gelesen und geschrieben.
Ungepufferte Ein- und Ausgabe eignet sich für schnelle Verarbeitung, während
gepufferte Ein- und Ausgabe insgesamt effizienter ist.
Die try-With-Resources-Anweisung erleichtert das Schließen von Strömen automatisch.
Ein- und Ausgabekanäle können rohe Daten (Bytes) übertragen, und Zeichen sind im
UTF-16-Format dargestellt.
Die Wahl der Kodierung ist wichtig, und Java bietet verschiedene Klassen und
Methoden, um die Kodierung anzupassen.
Java kann auf Shell-Variablen zugreifen, und die Runtime-Klasse bietet Methoden zur
Abfrage von Systeminformationen und zur Ausführung externer Prozesse.
Prozesssteuerung
Die Methode exec der Runtime-Klasse ermöglicht das Starten neuer Prozesse.
Der Zugriff auf Ein- und Ausgabeströme sowie das Warten auf Prozessbeendigung
sind wichtige Aspekte der Prozesssteuerung.
Diese Prinzipien bilden die Grundlage für den Umgang mit Dateien, I/O-Strömen und
externen Prozessen in Java.
Ziel
Berechnung von Primzahlen mithilfe des Siebs des Eratosthenes durch Prozesse,
die über Pipelines kommunizieren.
Funktionsweise:
Herausforderungen:
Verbesserungsvorschläge:
Die parallele Programmierung bietet zahlreiche Möglichkeiten zur weiteren Optimierung der
Laufzeit und Datenverarbeitung pro Zeiteinheit. Eine sorgfältige Analyse, Anpassung und
Experimentation sind entscheidend für die Entwicklung effizienter paralleler Programme.
Name:
Problemstellung:
Lösung:
Konsequenzen:
Liste der Eigenschaften der Lösung, die als Vor- und Nachteile betrachtet werden
können.
Ein und dieselbe Eigenschaft kann je nach Situation Vor- oder Nachteil sein.
Beispiel: Im Visitor-Pattern ersetzt dynamisches Binden unerwünschte dynamische
Typabfragen und verbessert die Wartbarkeit.
Faustregel:
6.1.2 Visitor
Leicht erweiterbar durch Definition neuer Untertypen von Visitor für neue
Operationen.
Zentrale Verwaltung von verwandten Operationen im Visitor, getrennt von Visitor-
fremden Operationen.
Möglichkeit zur Arbeit mit Objekten aus unabhängigen Klassen hierarchien.
Gute Erweiterungsmöglichkeit der Visitor-Klassen, aber schlechte Erweiterbarkeit der
konkreten Elemente, wenn neue hinzugefügt werden.
Die Art des Zugriffs auf konkrete Elemente durch visit-Methoden hängt von
Implementierungsdetails ab.
Iterator ermöglicht sequentiellen Zugriff auf Elemente eines Aggregats, ohne dessen
innere Darstellung offenzulegen.
Anwendbar, um auf Inhalte eines Aggregats zuzugreifen, mehrere Abarbeitungen zu
ermöglichen und eine einheitliche Schnittstelle für polymorphe Iterationen
bereitzustellen.
Implementierungsvarianten:
6.1.4 Template-Method
Implementierungsdetails:
Primitive Operationen sind häufig protected, abstrakte Methoden sind als abstract
deklariert.
"templateMethod" selbst sollte nicht überschrieben werden und kann final sein.
Ziel: Minimierung der Anzahl der zu überschreibenden primitiven Operationen für
einfachere Wiederverwendung.
Definition:
Anwendbarkeit:
Wenn eine Klasse Objekte erzeugen soll, deren Klasse nicht bekannt ist.
Wenn eine Klasse möchte, dass Unterklassen die Art der zu erzeugenden Objekte
bestimmen.
Um Verantwortlichkeiten an Unterklassen zu delegieren und das Delegieren lokal zu
halten.
Wenn die Allokation und Freigabe von Objekten zentral in einer Klasse verwaltet
werden sollen.
Struktur:
Produkt-Hierarchie: Abstrakte Klasse "Product" und konkrete Klasse
"ConcreteProduct".
Creator-Hierarchie: Abstrakte Klasse "Creator" mit abstrakter oder default
"factoryMethod".
ConcreteCreator: Unterklasse von "Creator" implementiert "factoryMethod".
Eigenschaften:
Implementierungsdetails:
Nachteil:
Definition:
Anwendbarkeit:
Wenn Klassen, von denen Objekte erzeugt werden sollen, erst zur Laufzeit bekannt
sind.
Um die Erzeugung einer Hierarchie von Creator-Klassen zu vermeiden (im Gegensatz
zur Factory-Method).
Wenn jedes Objekt einer Klasse nur wenige unterschiedliche Zustände haben kann.
Struktur:
Prototype: (Möglicherweise abstrakte) Klasse mit der Methode clone(), die sich selbst
kopiert.
ConcretePrototype: Konkrete Unterklassen von Prototype, die clone überschreiben.
Client: Verwendet die clone-Methode zur Erzeugung neuer Objekte.
Eigenschaften:
Implementierungsdetails:
Anmerkungen:
In Sprachen wie Java wird das Cloneable-Interface verwendet, um die Verwendung von
clone zu steuern.
Schwierigkeiten können bei der Initialisierung und Zustandsänderung nach der Kopie
auftreten.
Besonders sinnvoll in statisch typisierten Sprachen wie C++ und Java, weniger
notwendig in dynamisch typisierten Sprachen mit direkter Unterstützung für ähnliche
Funktionalität.
6.2.3 Singleton
Definition:
Das Singleton-Entwurfsmuster sichert zu, dass eine Klasse nur eine Instanz hat und
ermöglicht globalen Zugriff auf dieses Objekt.
Anwendbarkeit:
Wenn genau ein Objekt einer Klasse global zugreifbar sein soll.
Wenn die Klasse durch Vererbung erweiterbar sein soll und Anwender die erweiterte
Klasse ohne Änderungen verwenden können sollen.
Eigenschaften:
Implementierungsdetails:
Die Klasse enthält eine statische Methode instance, die das einzige Objekt zurückgibt.
Der Konstruktor ist privat, um die Instanziierung von außen zu verhindern.
Initialisierung erfolgt beim ersten Zugriff.
Schwierigkeiten können bei der Implementierung von verschiedenen Singleton-
Alternativen auftreten.
Feste Verdrahtung der Alternativen in der ursprünglichen Form kann unflexibel sein.
Beispiele:
Aktuelle Praxis:
6.3.1 Decorator
Definition:
Anwendbarkeit:
Struktur:
Component (Window): Definiert eine Schnittstelle für Objekte, an die
Verantwortlichkeiten dynamisch hinzugefügt werden können.
ConcreteComponent (WindowImpl): Konkrete Implementierung von Component.
Decorator (WinDecorator): Abstrakte Klasse für Verantwortlichkeiten, die
dynamisch hinzugefügt werden können.
ConcreteDecoratorA, ConcreteDecoratorB: Konkrete Implementierungen von
Decorator mit spezifischer Funktionalität.
Eigenschaften:
Verwendung:
Geeignet zur Erweiterung der Oberfläche oder des Erscheinungsbilds eines Objekts.
Weniger geeignet für inhaltliche Erweiterungen oder umfangreiche Objekte.
Implementierungsdetails:
Beispiele:
Definition:
Ein Proxy, auch Surrogate genannt, fungiert als Platzhalter für ein anderes Objekt und
kontrolliert den Zugriff darauf. Es gibt verschiedene Anwendungsmöglichkeiten für
Proxy-Objekte, darunter Remote-Proxies, Virtual-Proxies, Protection-Proxies und
Smart-References.
Anwendbarkeit:
Bei Bedarf für eine intelligentere Referenz auf ein Objekt als ein einfacher Zeiger.
In Situationen, in denen ein Objekt teuer in der Erzeugung ist und erst bei Bedarf
geladen werden soll.
Zur Kontrolle des Zugriffs auf Objekte basierend auf unterschiedlichen
Zugriffsrechten oder Situationen.
Remote-Proxies:
o Platzhalter für Objekte in anderen Namensräumen, z.B., auf anderen
Rechnern.
o Leiten Nachrichten über komplexe Kommunikationskanäle weiter.
Virtual-Proxies:
o Erzeugen Objekte bei Bedarf, um aufwendige Erzeugung zu vermeiden.
o Verzögern die Erzeugung, bis das Objekt tatsächlich benötigt wird.
Protection-Proxies:
o Kontrollieren den Zugriff auf Objekte basierend auf Zugriffsrechten.
o Verschiedene Zugriffsrechte je nach Zugreifer oder Situation.
Smart-References:
o Ersetzen einfache Zeiger und führen zusätzliche Aktionen bei Zugriffen durch.
o Beispiel: Referenzzählung, Laden von persistenten Objekten, Thread-
Sicherheit.
Struktur:
Abstrakte Klasse oder Interface Subject als gemeinsame Schnittstelle für RealSubject und
Proxy.
RealSubject: Definiert die eigentlichen Objekte, die durch Proxies repräsentiert werden.
Proxy: Kontrolliert den Zugriff auf das eigentliche Objekt, kann für Erzeugung oder
Entfernung verantwortlich sein.
Implementierungsdetails:
Unterschied zu Decorator:
Ein Proxy kontrolliert den Zugriff auf ein Objekt, während ein Decorator ein Objekt
um zusätzliche Verantwortlichkeiten erweitert.
Beispiele:
6.4 Entscheidungshilfen
Entwurfsmuster sollten nicht als festgelegte Regeln, sondern als Werkzeuge zur
Entscheidungsfindung verstanden werden.
Zwei Sichtweisen: Wertend (positive Eigenschaften betonend) und nicht wertend
(objektive Beschreibung von Problemen und Lösungen).
Fazit: