Titel | Inhalt | Suchen | Index | DOC | Handbuch der Java-Programmierung, 3. Auflage
<< | < | > | >> | API | Kapitel 9 - OOP III: Interfaces

## 9.5 Interfaces und Hilfsklassen

- 9.5 Interfaces und Hilfsklassen
  - 9.5.1 Die Default-Implementierung
  - 9.5.2 Delegation an die Default-Implementierung
  - 9.5.3 Die leere Implementierung

In den vorigen Abschnitten wurde gezeigt, daß es viele Gemeinsamkeiten zwischen (abstrakten) Klassen und Interfaces gibt. Während der Designphase eines komplexen Softwaresystems ist es daher häufig schwierig, sich für eine von beiden Varianten zu entscheiden. Für die Verwendung des Interfaces spricht die größere Flexibilität durch die Möglichkeit, in unterschiedlichen Klassenhierarchien verwendet zu werden. Für die Verwendung einer Klasse spricht die Möglichkeit, bereits ausformulierbare Teile der Implementation zu realisieren und die Fähigkeit, statische Bestandteile und Konstruktoren unterzubringen.

Wir wollen in diesem Abschnitt zeigen, wie sich beide Ansätze vereinen lassen. Dabei wird zunächst jeweils ein Interface zur Verfügung gestellt und seine Anwendung dann unter Verwendung einer Hilfsklasse vereinfacht.

### 9.5.1 Die Default-Implementierung

Wird ein Interface erstellt, das voraussichtlich häufig implementiert werden muß und/oder viele Methoden definiert, ist es sinnvoll, eine Default-Implementierung zur Verfügung zu stellen. Damit ist eine Basisklasse gemeint, die das Interface und alle sinnvoll realisierbaren Methoden implementiert. Besitzt eine Klasse, die das Interface implementieren muß, keine andere Vaterklasse, so kann sie von der Default-Implementierung abgeleitet werden und erbt so bereits einen Teil der sonst manuell zu implementierenden Funktionalität.

Als Beispiel soll ein Interface `SimpleTreeNode` definiert werden, das zur Konstruktion eines Baums verwendet werden kann, dessen Knoten von beliebigem Typ sind und jeweils beliebig viele Kinder haben:

    001 /* SimpleTreeNode.java */
    002
    003 public interface SimpleTreeNode
    004 {
    005   public void addChild(SimpleTreeNode child);
    006   public int getChildCnt();
    007   public SimpleTreeNode getChild(int pos);
    008 }
| SimpleTreeNode.java

Listing 9.15: Das SimpleTreeNode-Interface

Die Default-Implementierung könnte wie folgt realisiert werden:

    001 /* DefaultTreeNode.java */
    002
    003 public class DefaultTreeNode
    004 implements SimpleTreeNode
    005 {
    006   private int              CAPACITY;
    007   private String           name;
    008   private SimpleTreeNode[] childs;
    009   private int              childcnt;
    010
    011   public DefaultTreeNode(String name)
    012   {
    013     this.CAPACITY = 5;
    014     this.name     = name;
    015     this.childs   = new SimpleTreeNode[CAPACITY];
    016     this.childcnt = 0;
    017   }
    018
    019   public void addChild(SimpleTreeNode child)
    020   {
    021     if (childcnt >= CAPACITY) {
    022       CAPACITY *= 2;
    023       SimpleTreeNode[] newchilds = new SimpleTreeNode[CAPACITY];
    024       for (int i = 0; i < childcnt; ++i) {
    025         newchilds[i] = childs[i];
    026       }
    027       childs = newchilds;
    028     }
    029     childs[childcnt++] = child;
    030   }
    031
    032   public int getChildCnt()
    033   {
    034     return childcnt;
    035   }
    036
    037   public SimpleTreeNode getChild(int pos)
    038   {
    039     return childs[pos];
    040   }
    041
    042   public String toString()
    043   {
    044     return name;
    045   }
    046 }
| DefaultTreeNode.java

Listing 9.16: Default-Implementierung des SimpleTreeNode-Interfaces

|  | Der Vorteil ist hier noch nicht sehr offensichtlich, weil die Implementierung nicht sehr aufwendig ist. Bei komplexeren Interfaces zahlt es sich in der Praxis meistens aus, wenn eine Default-Implementierung zur Verfügung gestellt wird. Neben der dadurch erzielten Arbeitsersparnis nützt sie auch zur Dokumentation und stellt eine Referenzimplementierung des Interfaces dar. |  |
Hinweis

### 9.5.2 Delegation an die Default-Implementierung

Läßt sich eine potentielle `SimpleTreeNode`-Klasse nicht von `DefaultTreeNode` ableiten, muß sie das Interface selbst implementieren. Besitzt die Default-Implementierung bereits nennenswerte Funktionalitäten, wäre es schlechter Stil (und auch sehr fehlerträchtig), diese ein zweites Mal zu implementieren. Statt dessen ist es eventuell möglich, die Implementierung an die bereits vorhandene `DefaultTreeNode` zu delegieren.

Dazu muß die zu implementierende Klasse eine Membervariable vom Typ `DefaultTreeNode` anlegen und alle Aufrufe der Interface-Methoden an dieses Objekt weiterleiten. Soll beispielsweise die Klasse `Auto` aus Listing 7.1 in eine `SimpleTreeNode` verwandelt werden, könnte die Implementierung durch Delegation wie folgt vereinfacht werden:

    001 /* Auto5.java */
    002
    003 public class Auto5
    004 implements SimpleTreeNode
    005 {
    006   public String name;
    007   public int    erstzulassung;
    008   public int    leistung;
    009
    010   private SimpleTreeNode treenode = new DefaultTreeNode("");
    011
    012   public void addChild(SimpleTreeNode child)
    013   {
    014     treenode.addChild(child);
    015   }
    016
    017   public int getChildCnt()
    018   {
    019     return treenode.getChildCnt();
    020   }
    021
    022   public SimpleTreeNode getChild(int pos)
    023   {
    024     return treenode.getChild(pos);
    025   }
    026
    027   public String toString()
    028   {
    029     return name;
    030   }
    031 }
| Auto5.java

Listing 9.17: Implementierung des SimpleTreeNode-Interfaces durch Delegation

Hier nutzt die Klasse `Auto5` die Funktionalitäten der Membervariable `DefaultTreeNode` zur Verwaltung von Unterknoten und leitet alle entsprechenden Methodenaufrufe an sie weiter. Die Verwaltung des Knotennamens erfolgt dagegen mit Hilfe der eigenen Membervariable `name`.

### 9.5.3 Die leere Implementierung

Mitunter wird ein Interface entworfen, bei dem nicht immer alle definierten Methoden benötigt werden. In der Java-Klassenbibliothek gibt es dafür einige Beispiele, etwa bei Listenern (siehe Kapitel 28) oder im Collection-API (siehe Kapitel 15). Da bei der Implementierung aber immer alle definierten Methoden implementiert werden müssen, wenn die Klasse nicht abstrakt bleiben soll, kann es nützlich sein, eine leere Standard-Implementierung zur Verfügung zu stellen. Implementierende Klassen können sich dann gegebenenfalls von dieser ableiten und brauchen nur noch die Methoden zu realisieren, die tatsächlich benötigt werden.

Titel | Inhalt | Suchen | Index | DOC | Handbuch der Java-Programmierung, 3. Auflage, Addison Wesley, Version 3.0
<< | < | > | >> | API | © 1998-2002 Guido Krüger, http://www.javabuch.de