Vererbung - nicolasruh

Werbung
Warum Programmieren lernen?
You do not really understand
something unless you can explain it
to your grandmother computer.
-
Albert Einstein

Programmieren lernen ist komplex
 ... wie das Erlernen einer normalen Sprache (Syntax & Vokabular)
 Zwei Prinzipien zum Umgang mit Komplexität:
Iteration
Abstraktion
Iteration
•
Komplexe Dinge kann man nicht von vorn nach hinten lernen
•
Wichtige Konzepte werden wieder und wieder auftauchen
•
Sie werden diese Konzepte besser und besser verstehen
•
Eigene Erfahrungen machen, bzw. Anwenden und Üben, ist ein
essenzieller Teil dieses Prozesses
Abstraktion
 Verschiedene Perspektiven
 Wechsel zwischen Wie? und Warum?, zwischen Anwenden und
Analysieren
 Allgemeine Prinzipien finden
 Visualisierungen (Grafiken, Diagramme, etc.) sind hilfreich
 Analogien sind hilfreich

Java...
... ist wie ein Lego Technik Baukasten
Beim Programmieren geht es darum...
... die Bauteile so
zusammenzubauen,
dass das Modell
funktioniert ...
... und fehlende Bauteile zu definieren
•
Bauteil = Objekt
•
Definition eines Bauteils =
Klasse
•
Was das Bauteil kann =
Methoden
•
Anleitung zur Benutzung
der Bauteile = API
•
Sammlung von Bauteilen =
Bibliothek
Greenfoot ist ...
•
Ein GUI, das mit Java
programmiert wurde
 Sie müssen herausfinden,
wie man das GUI benutzt
•
Eine IDE, in der Sie zwei
vordefinierte Klassen (World und Actor) mit
allem erweitern können, was die Sprache Java
zu bieten hat
 Sie müssen herausfinden, wie Sie dem
Computer sagen, was die Welt und die
Akteure tun sollen (in Java)
Das GUI von Greenfoot
(ist übrigens selbst ein in Java definiertes Objekt)
Projekt, package
(Dateiformat?,
Speicherort?)
== README.txt
Die beiden
erweiterbaren
Klassen
englische APIs
für Greenfoot &
Java (online)
Programmablauf
beobachten,
Objekte und
Methoden
testen
Editor = IDE
Kompilieren???
Der Editor (IDE) von Greenfoot
Kompilieren???
(hier braucht es
die JDK)
documentation
= API für diese
Klasse (aus
Kommentaren)
Name der Klasse
Eine Methode
Formatierung
(syntax checking
highlighting,
indentation,
undo, ...)
kompilieren = erstellen, übersetzen, zusammentragen
Programmiersprache
(Quellcode, Text, für
Menschen verarbeitbar)
Compiler
Maschinensprache
(Binärcode, Nullen & Einsen,
für Prozessoren verarbeitbar)
Programm in C
CCompiler
CCompiler
CCompiler
kompilieren = erstellen, übersetzen, zusammentragen
Sprache = Java
(Quellcode, Text, für
Menschen verarbeitbar)
Java-Compiler
Byte-Code
(für JRE/JVM verarbeitbar)
Maschinensprache
(Binärcode, Nullen & Einsen,
für Prozessoren verarbeitbar)

Arrays & Datenstrukturen
Datenstruktur = mehrere Werte
(unter einem Variablennamen)
•
•
•
•
In Java ist eine
Datenstruktur
eigentlich eine Klasse,
deren Aufgabe es ist,
mehrere Werte zu
verwalten
Der Oberbegriff (und
auch das Interface)
dafür ist „Collections“
Die Abbildung rechts
zeigt nur einige davon
Den klassischen Array gibt es hauptsächlich aus historischen
Gründen, er ist einfach, aber unsauber
Arrays
•
Ein Array ist eine (unsaubere) Datenstruktur
•
Ein Array ist so etwas wie ein Regal, in dessen Fächern
jeweils ein Wert gespeichert wird
•
In einem Array kann man mehrere Daten gleichen Typs
unter einem Namen speichern
•
In Java können Arrays ihre Grösse (=Anzahl Elemente)
NICHT ändern
•
Leere Elemente haben den Wert null
•
Das erste Element hat den Index 0!
Array:
typ[] name = {Werte};
Regal mit gleichartigen Kisten:
name[0]  Inhalt der ersten Kiste
Beispiel:
int[] arr = {3, 5, 0, 17};
// leeres Array: int[] arr = new int[4];
System.out.println(arr[3]);
//  17
arr[1] = 11; // an 2. Stelle 11 statt 5
int x = arr[0];
// x ist 3
int l = arr.length; // l ist 4
ObjektOrientiertes Programmieren
Konzepte, Begriffe, Beispiele – und ein wenig UML

Konzepte des OOP
 Datenkapselung
 Vererbung
 Überschreiben
 Überladen
 Polymorphie
Datenkapselung
 ist eines der wichtigsten Grundprinzipien des OOP.
 bezeichnet den kontrollierten/eingeschränkten
Zugriff auf Methoden bzw. Attribute von Klassen
 wird in JAVA durch Zugriffsmodifizierer umgesetzt,
die die Sichtbarkeit (und damit Aufrufbarkeit) für
externe Klassen kontrollieren:
 public (öffentlich)  von außen für jeden sichtbar
 private (privat)  nur intern (in der Klasse) sichtbar
Datenkapselung
 Das Ziel eines sauberen Klassendesigns ist es zu
erreichen, dass Klassen bzw. Objekte nur über
wenige, wohl-definierte Schnittstellen (= öffentliche
Methoden) mit anderen Klassen interagieren.
 Vom Innenleben einer Klasse soll der Verwender
(Client-Klassen bzw. auch der Programmierer)
möglichst wenig wissen müssen (Geheimnisprinzip).
 So kann Software maximal modularisiert werden 
überschaubar, stabil, flexibel & erweiterbar
Regeln für Zugriffsmodifizierer:
 Der Zugriff auf Bestandteile einer Klasse sollte
immer maximal eingeschränkt werden ("so privat
wie möglich").
 Instanzvariablen (d.h. Attribute) werden private
deklariert. Zugriff von ausserhalb der Klasse erfolgt
über public getter- und setter-Methoden.
 so kann man zwischen Lese- und Schreibrechten
unterscheiden
 Konstruktoren werden public deklariert.
Faustregeln für Methoden
 keine Methode sollte mehr als 10 Zeilen Code enthalten,
sonst in mehrere Aufteilen
 jede Methode sollte genau eine Aufgabe haben, dann ist
sie auch einfach zu benennen
 Gut organisierten Code kann man fast wie normalen Text
lesen (dann muss man auch weniger kommentieren)
 Methoden sollten möglichst eigenständig sein
 Methoden sollten nur dann public sein, wenn es nicht
anders geht (= Schnittstellen mit anderen Klassen)
Gründe für Methoden
Code organisieren durch Unterteilung in kleine,
wiederverwertbare, eigenständige Einheiten
 Code einsparen
 durch wiederverwendbare Methoden
 Code übersichtlich gestalten
 durch geschickt benannte Methoden
 Code flexibel gestalten
 durch Methoden mit Übergabewerten
 Geheimnisprinzip (Sichtbarkeit einschränken)
Vorteile der Datenkapselung
 Weniger unerwünschten Interaktionen zwischen
Programmteilen, dadurch weniger Bugs
 Erhöhte Übersichtlichkeit, da meist nur die
öffentliche Schnittstelle einer Klasse (API) betrachtet
werden muss
 Erhöhte Flexibilität durch Modularität, einzelne
Klassen oder Methoden können verändert oder
ausgetauscht werden, ohne den Rest des Programms
zu beeinflussen.
Vererbung in Java
 Eine Unterklasse erbt alle Eigenschaften (Attribute und
Methoden) einer Oberklasse.
IST-Beziehung
Beispiel Vererbung
+ ! zeichneKreis
Buntstift erweitert die Funktionalität von Stift
(Spezialisierung)
Beispiel Vererbung
class Figurstift extends Buntstift
{
public Figurstift()
{
super();
//Konstruktor der Elternklasse benutzen
}
public void zeichneQuadrat(double s)
{
zeichneRechteck(s, s); //muss auch in Figurstift
sein
}
}
Vererbungshierarchie
 Wenn man dieselbe Methode
in verschiedene Klassen
kopiert, ergeben sich
Probleme – spätestens wenn
man die multiplen Kopien
ändern will
 Meist hätte man das besser
mit Vererbung gelöst, also
nur eine Version der
Methode in einer
gemeinsamen Elternklasse.
Vererbungshierarchie
Alle Klassen erben von Object
"super"
 Methode der Oberklasse aufrufen:
super.methodenName();
Normalerweise werden Methoden in Oberklassen automatisch gefunden.
super braucht man nur, wenn dieselbe Methode in der eigenen Klasse (this)
auch existiert (also überschrieben wurde), man aber diejenige der
Oberklasse aufrufen will
 Konstruktor der Oberklasse aufrufen:
super();
Da es in allen Klassen immer einen Konstruktor gibt (er also immer
überschrieben ist), benötigt man das super() oft, um denn Konstruktor der
Oberklasse aufzurufen
"this"
 this wird gebraucht, wenn eine Instanz einen
Auftrag an sich selber schickt (kann man meist weg
lassen), oder wenn die Instanz sich selbst als
Parameter übergeben will
onMousePressed(this);
Vorteile der Vererbung
 Erhöhte Übersicht in einem Klassendesign
 Durch die Vererbung lassen sich logische Hierarchien
abbilden
 Weniger Quellcode nötig
 Code der Oberklasse wird in Unterklassen
wiederverwendet
 Einfachere Wartung
 Änderungen müssen nur an einer Stelle durchgeführt
werden
Vorteile der Vererbung (Beispiel)
- name, adresse, telefon müssen nur einmal programmiert werden.
- Ein neues Attribut alter muss nur an einer Stelle eingefügt werden.
Überschreiben
 wenn Methoden oder Attribute in Kind- UND
Elternklasse definiert sind
(die überschriebene Methode kann mit super.f() noch immer aufgerufen werden)
Vorteile des Überschreibens
 Im Unterschied zur Vererbung (Erweiterung der
Elternklasse) kann man durch Überschreiben Teile
des Verhaltens der Elternklasse verändern
 Ein Beispiel ist die Methode toString(), die bereits in
der grundlegendsten aller Java-Klassen (Object)
definiert ist, dann aber in fast allen Unterklassen
erneut definiert wird
 um einen String zu erzeugen, der das Wichtigste
über diese Instanz in druckbarer Form angibt
Überladen
 mehrere Versionen einer Methode, die sich nur in
Typ und/oder Anzahl der Übergabeparameter
unterscheiden
 häufig beim Konstruktor eingesetzt
 die zum Aufruf passende Version wird vom Compiler
automatisch erkannt
Beispiel Überladen
class Figurstift {
Color col;
public Figurstift()
{
col = new Color(255, 0, 0); //default-Wert für col
}
public Figurstift(Color initcol)
{
col = initcol; //bei Erschaffung übergebener Wert
//z.B. new Figurstift(new Color(255,0,0));
}
}
Vorteile des Überladens
 Implementierung von optionalen
Übergabeparametern (default values)
 Zeit- und Tipparbeit-Ersparnis beim Gebrauch einer
Klasse durch die Definition mehrerer Konstruktoren.
 Setzen von Attributen beim Erschaffen der Instanz
Polymorphie
(griechisch: Vielgestaltigkeit)
 beschreibt die Fähigkeit eines Bezeichners, abhängig von
seiner Verwendung unterschiedliche Datentypen
anzunehmen.
 Jedes Objekt kann auch den Typ seiner Elternklasse(n)
annehmen

jedes Java-Objekt hat die Grundklasse Object
 Objekte können in kompatible Typen gecastet werden,
z.B.
int i = 1;
double d = (double) i;
Joe joe1 = new Joe();
Actor a = (Actor) joe1;
Object o = (Object) joe1;
Beispiel Polymorphie
public class Polymorphie {
double flaeche = 0;
Rechteck re1 = new Rechteck( 3, 4 );
Figur re2 = new Rechteck( 5, 6 );
Kreis kr1 = new Kreis( 7 );
Figur kr2 = new Kreis( 8 );
Vector vec = new Vector(re1, re2, kr1, kr2);
// Berechne die Summe der Flaechen aller Figuren:
for( int i=0; i<vec.size(); i++ ) {
Figur f = (Figur)(vec.get( i ));
flaeche += f.getFlaeche(); //benutzt die (evtl. über//schriebene) Methode getFlaeche der jeweiligen Unterklasse
}
System.out.println( "Gesamtflaeche ist: " + flaeche ); }
}
Vorteile von Polymorphie
 einheitlicher Aufruf von überschriebenen Methoden
(z.B. toString())
 dynamische Typumwandlung macht Vererbung erst
effizient nutzbar
 insgesamt spart man dadurch Tipp- und
Organisationsaufwand und bekommt
übersichtlicheren Sourcecode
ObjektOrientiertes Programmieren ...
... ist hauptsächlich UML

OOP-Begriffe

Abstraktion

Reduktion der Wirklichkeit auf das
Wesentliche

Bauplan für Objekte

Modellieren

UML (Unified Modeling Language)
Standardisierte Art, das Modell in
Diagrammen darzustellen
Fachbereich des Software-Architekten
Objekt == Instanz
Konkrete Realisierung (Instanziierung)
einer Klasse
Die Abstraktion in einem SoftwareModell beschreiben

Klasse
Attribut == Instanzvariable
Eigenschaft eines Objekts

Methode
Fähigkeit eines Objekts
Softwareentwicklung
Code schreiben
Code organisieren
 spez. Programmiersprache
 UML
 einfüllen des Codes im Body
 planen der Interaktion &
von Methoden
Schnittstellen von Klassen
 „Codemonkeys“
 Systemarchitekten
Code schreiben ist einfach, Code organisieren ist schwer
(und deutlich besser bezahlt)
UML =
unified modeling language
UML-Klassendiagramm
 stellt den Aufbau der einzelnen Klassen dar (wie JavaDoc)
 stellt Zusammenhänge zwischen Klassen dar
Aufbau von Klassen
Rectangle
Modifikator
+ für public
- für private
Typ des Übergabewerts
Typ des Rückgabewerts
-length : double
-width : double
+setLength(len : double) : void
+setWidth(w : double) : void
+getLength() : double
+getWidth() : double
+getArea() : double
Name der Klasse
Attribute der Klasse
Methoden der Klasse
Zusammenhänge zwischen Klassen
class B extends A {
…
}
Vererbung
B „ist ein“ A
B „ist Spezialisierung von“ A
class B implements A {
…
}
Realisierung eines Interface
class A {
B b;
}
Abhängigkeit
class A {
B.init();
}
A „hat/benötigt/benutzt“ B
s. UML-Symbole.doc oder
UML/Klassendiagramm.pdf
class A {
B b;
}
gerichtete Assoziation
class A {
B b = new B(this);
void xxx() {…}
… b.yyy(); …
}
bidirektionale Assoziation
class B {
A a;
public B(A a) {
this.a = a;
}
public yyy() {…}
… a.xxx(); …
}
class A {
B b;
}
A „verwendet“ B
A „verwendet“ B
B „verwendet“ A
Jede Seite muss eine Referenz der anderen Seite
haben. Problem einer Inkonsistenz!
A verwendet B. Wenn B auch A verwendet
(indem B auf Datenfelder oder Methoden des
Objektes A zugreift), muss B eine Referenz auf
das Objekt A haben ("Callback").
Komposition
A „hat/besteht aus“ B
B ist ohne A nicht existent und nicht zu benutzen.
Eine Klasse erweitern
 class A extends B (Vererbung)
 Unterklasse A erbt alle Methoden von Oberklasse B
 A ist also eine spezialisierte Form von B („isA“)
 Java erlaubt nur eine einzige Oberklasse
 class A implements I (Interface)
 Das Interface I definiert Methoden (ohne Code)
 A wird gezwungen, die in I definierten Methoden zu
haben
 Eine Klasse kann mehrere Interfaces implementieren
Eine Verbindung schaffen
 Damit eine Instanz von A mit einer Instanz von B
reden kann, braucht sie einer Referenz zu b
class A {
B b;
MethodeX/Konstruktor () {
b = new B();
}
}
class B {
in MethodeX/Konstruktor () {
new A(this);
}
}
class A {
B b;
MethodeX/Konstruktor (B b) {
this.b = b;
}
}
 Auch indirekte Verbindungen über eine dritte Klasse
C sind möglich
Aufgabe
 Zeichnen Sie ein möglichst vollständiges UML-
Klassendiagramm des Joe2.0 Szenarios
(einschliesslich der benutzten Greenfoot-Klassen)
 Ist Greenfoot eher Library oder Framework?
externe Bibliotheke
 eine oder mehrere zur Verfügung gestellte Klassen,
oft mit internen Abhängigkeiten ( UML)
Es gibt zwei Möglichkeiten (oft gemischt):
1. Eigene Klassen erweitern Fremde (=Framework)
class meineKlasse extends fremdeKlasse {}
class meineKlasse implements fremdeKlasse{}
2. Eigene Klassen benutzen Fremde (=Library)
import fremdesPaket.fremdeKlasse;
FremdeKlasse xxx = new FremdeKlasse();
Software Development

Wasserfallmodell
Requirements
Design
Implementation
Verification
Maintenance
Iterative Development
Design
Requirements
Implementation
Initial Idea
Iteration
Planning
Testing
Evaluation
Deployment
Test Driven Development
Design
Requirements
run test and
see if it fails
(as Tests)
Initial Idea
write code to
cover the test
add a test
Planning
Implementation
refactor
run again and
see if it passes
Testing
(automated)
Evaluation
Deployment
Schlussfolgerungen:
•
Mehrstufige Grobplanung
•
Detailüberlegungen nur für die gegenwärtige Stufe – nicht zu weit
voraus planen
•
Tests für alles, was nicht trivial ist
•
Tests als erstes  Formulierung der Anforderungen, explizite
Zielvorstellung
•
Jeweils nur an einem Problem arbeiten – auch hier helfen Tests,
nacheinander abarbeiten
•
Keine Angst vor refactoring/überarbeiten – Werkzeuge benutzen
Herunterladen