====== 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(); } } }