====== Kapitel 10: Vererbung ======
{{:schule:klassen:2017:10b:pasted:20180613-101206.png?300}}
In einem kleinen Demo-Programm bewegen sich Autos und Motorräder von links nach rechts über das Fenster. Zusätzlich fliegen Sterne in Parabelbahnen.
===== a) Schlechte Implementierung =====
Es bietet sich an, Klassen ''Stern'', ''Auto'' und ''Motorrad'' für die bewegten Objekte zu erstellen sowie eine zusätzliche Klasse ''AnimationStarter'', in der
* ständig neue bewegte Objekte instanziert werden,
* alle auf dem Bildschirm befindlichen Objekte ständig weiterbewegt werden und
* alle Objekte, die aus dem Bildschirm herausfliegen, vernichtet werden.
==== Klasse Stern ====
public class Stern {
private Sprite sprite;
private double vx, vy, ay;
public Stern(double x, double y) {
this.vx = Math.random() * 6 - 3;
this.vy = Math.random() * (-15) - 5;
this.ay = 0.3;
sprite = new Sprite("/vererbung/star.png", x, y);
sprite.strecken(0.2);
}
public void zeitschritt(){
vy += ay;
if(sprite != null) {
sprite.verschieben(vx, vy);
}
}
public boolean istAußerhalbDesFensters(){
return sprite.istAußerhalbDesFensters();
}
public void vernichten(){
sprite.vernichten();
}
}
==== Klasse Auto ====
public class Auto {
private Sprite sprite;
private double vx, vy, ay;
public Auto(double x, double y) {
this.vx = Math.random() * 3 + 1;
this.vy = 0;
this.ay = 0;
sprite = new Sprite("/vererbung/car2.png", x, y);
sprite.strecken(0.2);
}
public void zeitschritt(){
vy += ay;
if(sprite != null) {
sprite.verschieben(vx, vy);
}
}
public boolean istAußerhalbDesFensters(){
return sprite.istAußerhalbDesFensters();
}
public void vernichten(){
sprite.vernichten();
}
}
==== Klasse Motorrad ====
public class Motorrad {
private Sprite sprite;
private double vx, vy, ay;
public Motorrad(double x, double y) {
this.vx = Math.random() * 6 + 2;
this.vy = 0;
this.ay = 0;
sprite = new Sprite("/vererbung/motorbike.png", x, y);
sprite.strecken(0.2);
}
public void zeitschritt(){
vy += ay;
if(sprite != null) {
sprite.verschieben(vx, vy);
}
}
public boolean istAußerhalbDesFensters(){
return sprite.istAußerhalbDesFensters();
}
public void vernichten(){
sprite.vernichten();
}
}
==== Klasse AnimationStarter ====
public class AnimationStarter implements TimerListener {
private ArrayList sterne = new ArrayList<>();
private ArrayList autos = new ArrayList<>();
private ArrayList motorräder = new ArrayList<>();
int i = 0;
public static void main(String[] args) {
new AnimationStarter().start();
}
private void start() {
Fenster f = new Fenster(800, 750);
Timer timer = new Timer(this, 20);
timer.start();
}
@Override
public void timerSignalVerarbeiten() {
i++;
if(i % 10 == 0){
sterne.add(new Stern(300, 600));
autos.add(new Auto(0, Math.random() * 600));
motorräder.add(new Motorrad(0, Math.random() * 600));
}
for(int i = 0; i < autos.size(); i++){
Auto a = autos.get(i);
if(a.istAußerhalbDesFensters()){
autos.remove(a);
a.vernichten();
}
a.zeitschritt();
}
for(int i = 0; i < sterne.size(); i++){
Stern s = sterne.get(i);
if(s.istAußerhalbDesFensters()){
sterne.remove(s);
s.vernichten();
}
s.zeitschritt();
}
for(int i = 0; i < motorräder.size(); i++){
Motorrad m = motorräder.get(i);
if(m.istAußerhalbDesFensters()){
motorräder.remove(m);
m.vernichten();
}
m.zeitschritt();
}
}
}
==== Bewertung der Implementierung ====
Die Implementierung hat folgende Nachteile:
* Große Teile des Codes in den Klassen ''Stern'', ''Auto'' und ''Motorrad'' sind identisch.
* Die Erstellung der Klassen ist recht zeitaufwändig
* Will man etwas in den betroffenen Codeblöcken ändern, so muss man die Änderungen in allen drei Klassen vornehmen
* In der Klasse ''AnimationStarter'' müssen für Autos, Sterne und Motorräder jeweils extra Listen geführt werden. Dies hat zur Folge, dass sich in der Methode ''timerSignalVerarbeiten'' große Teile des Codes wiederholen.
===== Bessere Implementierung durch Nutzung von Vererbung =====
Die gemeinsamen Methoden und Attribute der Klassen ''Stern'', ''Auto'' und ''Motorrad'' packen wir in eine **Basisklasse** ''BewegteFigur''. Die Klassen ''Stern'', ''Auto'' und ''Motorrad'' leiten wir dann von ''BewegteFigur'' ab, d.h. sie **erben** alle Attribute und Methoden der Basisklasse.
* Dass eine Klasse von einer anderen Klasse ('**Basisklasse**') abgeleitet ist, wird durch das Schlüsselwort **extends** ausgedrückt.
* Der Konstruktor der Basisklasse lässt sich aus dem Konstruktor der abgeleiteten Klasse mit den Schlüsselwort **super** aufrufen, siehe die nachfolgenden Quelltexte.
==== Die Klasse BewegteFigur ====
public class BewegteFigur {
private Sprite sprite;
private double vx, vy, ay;
public BewegteFigur(String dateiname, double x, double y, double vx, double vy, double ay) {
this.vx = vx;
this.vy = vy;
this.ay = ay;
sprite = new Sprite(dateiname, x, y);
sprite.strecken(0.2);
}
public void zeitschritt(){
vy += ay;
if(sprite != null) {
sprite.verschieben(vx, vy);
}
}
public boolean istAußerhalbDesFensters(){
return sprite.istAußerhalbDesFensters();
}
public void vernichten(){
sprite.vernichten();
}
}
==== Die Klasse SternNeu ====
public class SternNeu extends BewegteFigur {
public SternNeu(double x, double y){
// Aufruf des Konstruktors der Basisklasse BewegteFigur:
super("/vererbung/star.png", x, y,
Math.random() * 6 - 3, Math.random() * (-15) - 5,
0.3);
}
}
==== Die Klasse AutoNeu ====
public class AutoNeu extends BewegteFigur {
public AutoNeu(double x, double y){
super("/vererbung/car2.png", x, y,
Math.random() * 3 + 1, 0,0);
}
}
==== Die Klasse MotorradNeu ====
public class MotorradNeu extends BewegteFigur {
public MotorradNeu(double x, double y){
super("/vererbung/motorbike.png", x, y,
Math.random() * 6 + 2, 0,0);
}
}
==== Die Klasse AnimationStarterNeu====
public class AnimationStarterNeu implements TimerListener {
private ArrayList figuren = new ArrayList<>();
int i = 0;
public static void main(String[] args) {
new AnimationStarterNeu().start();
}
private void start() {
Fenster f = new Fenster(800, 750);
Timer timer = new Timer(this, 20);
timer.start();
}
@Override
public void timerSignalVerarbeiten() {
i++;
if(i % 10 == 0){
figuren.add(new SternNeu(300, 600));
figuren.add(new AutoNeu(0, Math.random() * 600));
figuren.add(new MotorradNeu(0, Math.random() * 600));
}
for(int i = 0; i < figuren.size(); i++){
BewegteFigur f = figuren.get(i);
if(f.istAußerhalbDesFensters()){
figuren.remove(f);
f.vernichten();
}
f.zeitschritt();
}
}
}