Filter als Klassen
Du hast jetzt ein Dutzend Filter geschrieben, und alle stehen als Methoden
nebeneinander in Main.java. Das funktioniert, hat aber zwei Schwächen.
Zum einen bekommt jeder Filter seine Einstellungen als Parameter mitgereicht –
schwelle(grau, 128), heller(grau, 60). Eine Liste von Filtern lässt sich so
nicht bilden, weil jeder anders aufgerufen wird.
Zum anderen wird das Hintereinanderschalten unübersichtlich, sobald es mehr als zwei sind:
leinwand.zeige(schwelle(kanten(weich(heller(grau, 30))), 40));
Das liest sich von innen nach außen, also gegen die Reihenfolge, in der es passiert. Beides lösen wir jetzt mit dem, was du über Vererbung und abstrakte Klassen gelernt hast.
Ein Filter wird ein Objekt
Alle Filter haben eines gemeinsam: Sie machen aus einem Gitter ein neues. Das ist die gemeinsame Schnittstelle, und mehr braucht die Oberklasse nicht.
public abstract class Filter {
public abstract int[][] anwenden(int[][] pBild);
}
Was ein Filter darüber hinaus braucht – eine Grenze, eine Anzahl Stufen –, speichert er in eigenen Attributen und bekommt es im Konstruktor.
Öffne im Programmierbereich nacheinander Filter.java, Heller.java,
Invertieren.java und Schwelle.java. Der Rechenteil in jedem anwenden ist
derselbe, den du schon geschrieben hast – nur steht er jetzt in einer eigenen
Klasse, und die Einstellung kommt aus einem Attribut statt aus einem Parameter.
Drei Dinge sind hier passiert:
-
Die Einstellung wandert ins Objekt.
new Schwelle(128)merkt sich die Grenze. Danach sieht der Aufruf bei jedem Filter gleich aus:f.anwenden(bild). -
Gemeinsames wandert in die Oberklasse.
leeresWieundbegrenztstehen einmal inFilterstatt fünfmal in den Unterklassen. Sie sindprotected, gehören also den Unterklassen, aber nicht der Außenwelt. -
Der Typ ist
Filter, das Objekt ist eine Unterklasse. In der ZeileFilter aufhellen = new Heller(60);stehen beide. Welchesanwendenausgeführt wird, entscheidet sich erst zur Laufzeit – das ist Polymorphie.
In der Online-IDE wird @Override vor der überschreibenden Methode nicht
verwendet. In echtem Java gehört es dorthin; es ändert nichts am Ablauf, lässt
den Übersetzer aber prüfen, ob wirklich etwas überschrieben wird.
Aufgabe 1: Die Filterkette
Jetzt zahlt sich die gemeinsame Oberklasse aus: Filter lassen sich in ein Feld legen und der Reihe nach anwenden.
Ergänze im Hauptprogramm die Methode
int[][] kette(Filter[] pFilter, int[][] pBild)
Sie wendet die Filter in der Reihenfolge des Feldes an und liefert das Ergebnis. Erprobt wird sie mit
Filter[] scherenschnitt = { new Weichzeichnen(), new Kanten(), new Schwelle(40) };
Die Klassen Weichzeichnen und Kanten liegen im Programmierbereich als
leere Hüllen bereit. Ihr Rechenteil steht schon fertig auf den Seiten
Weichzeichnen und Kanten finden –
du musst ihn nur noch in die Form einer Klasse bringen.
Tipp 1: Die Schleife
Die Kette braucht eine Variable, die das Zwischenergebnis hält. Am Anfang ist das das Ausgangsbild, danach jedes Mal das Ergebnis des letzten Filters.
Tipp 2: Woher kommt der Typ?
pFilter[i] hat den Typ Filter, und Filter kennt anwenden. Mehr musst du
nicht wissen – ob dort ein Schwelle- oder ein Kanten-Objekt steckt, geht die
Kette nichts an. Genau das ist der Gewinn.
Aufgabe 2: Ein Filter, der Filter enthält
Die Unscharfmaskierung von der Seite Weichzeichnen braucht selbst einen Weichzeichner. Als Klasse geschrieben, enthält sie also einen anderen Filter als Attribut.
a) Fülle die Klasse Schaerfen im Programmierbereich aus. Sie legt sich im
Konstruktor ein Weichzeichnen-Objekt an und benutzt es in anwenden.
b) Gib ihr einen zweiten Konstruktor, dem man den inneren Filter übergeben kann. Was gewinnst du damit?
c) Warum ist das keine Vererbung? Beschreibe in einem Satz den Unterschied
zwischen „Schaerfen ist ein Filter" und „Schaerfen hat einen
Filter". Beides trifft hier zu.
Zum Weiterdenken
In Aufgabe 1 steht die Kette als Methode im Hauptprogramm. Schreibe sie als
Klasse Kette, die selbst von Filter erbt und im Konstruktor ein
Filter[] bekommt.
a) Was kannst du danach, was vorher nicht ging? Denk an Aufgabe 2.
b) Kann eine Kette eine andere Kette enthalten? Was bedeutet das für die
Tiefe, in der Filter geschachtelt werden können?
c) Dieses Muster – eine Klasse, die dieselbe Schnittstelle erfüllt wie die Dinge, die sie enthält – hat einen Namen. Such ihn und schau dir an, wo es sonst noch vorkommt. Dateiordner und Dateien sind ein bekanntes Beispiel.