2009-07-30 9 views

Antwort

8

Die kurze Antwort ist, es gibt nichts in Java so nah wie Sie möchten, aber es gibt Alternativen. Das Delegiertenmuster ist nicht schwer zu implementieren, es ist einfach nicht so praktisch wie mit Objective-C. Der Grund "informelle Protokolle" in Objective-C funktioniert, weil die Sprache unterstützt Kategorien, mit denen Sie Methoden zu vorhandenen Klassen hinzufügen, ohne Unterklasse sie oder sogar Zugriff auf den Quellcode haben. Daher sind die meisten informellen Protokolle eine Kategorie für NSObject. Dies ist in Java eindeutig unmöglich.

Objective-C 2.0 entscheidet sich für @optional Protokoll-Methoden, die eine viel sauberere Abstraktion ist und für neuen Code, aber noch weiter von einem Äquivalent in Java.

Ehrlich gesagt, ist der flexibelste Ansatz, ein Delegate-Protokoll zu definieren, dann haben Klassen implementieren alle Methoden. (Bei modernen IDEs wie Eclipse ist das trivial.) Viele Java-Schnittstellen haben eine begleitende Adapterklasse. Dies ist ein gängiger Ansatz, da der Benutzer nicht viele leere Methoden implementieren muss, aber die Vererbung einschränkt, was das Code-Design unflexibel macht . (Josh Bloch spricht das in seinem Buch "Effective Java" an.) Mein Vorschlag wäre, zuerst eine Schnittstelle bereitzustellen und dann einen Adapter hinzuzufügen, wenn es wirklich notwendig ist.

Was auch immer Sie tun, vermeiden Sie werfen UnsupportedOperationException für "nicht implementiert" Methoden. Dies zwingt die delegierende Klasse, Ausnahmen für Methoden zu behandeln, die optional sein sollten. Der richtige Ansatz besteht darin, eine Methode zu implementieren, die nichts tut, einen Standardwert zurückgibt usw. Diese Werte sollten für Methoden, die keinen void-Rückgabetyp aufweisen, gut dokumentiert sein.

2

Es gibt nichts, was Sie daran hindert, das Delegatenmuster in Ihren Java-Objekten zu verwenden (es ist einfach kein allgemein verwendetes Muster im JDK wie in Cocoa). Haben Sie einfach einen delegate ivar eines Typs, der Ihrer WhateverDelegate Schnittstelle entspricht, dann leiten Sie den Methodenaufruf in Ihren Instanzmethoden, die Sie delegieren möchten, an das Delegatobjekt weiter, falls es existiert. Sie würden wahrscheinlich mit etwas enden, das wie this aussieht, außer in Java anstelle von Obj-C.

Soweit optionale Schnittstellen gehen, wäre das schwieriger. Ich würde vorschlagen, die Schnittstelle zu deklarieren, eine abstrakte Klasse zu deklarieren, die optionale Methoden als leere Methoden implementiert, und dann die abstrakte Klasse zu unterklassifizieren und dabei die optionalen Methoden außer Kraft zu setzen, die dieses bestimmte Objekt implementieren soll. Aufgrund der fehlenden Mehrfachvererbung in Java gibt es hier möglicherweise eine schwerwiegende Einschränkung, aber das ist so nahe, wie ich es mir hätte vorstellen können.

+4

Werfen 'UnsupportedOperationException' ist eine schreckliche Idee! APIs sollten Benutzer niemals dazu zwingen, Ausnahmen in normalen Verwendungsmustern zu behandeln, nur für einen außergewöhnlichen Fluss. Das Delegatmuster in Cocoa ist insofern robust, als es stillschweigend nicht implementierte Delegate-Methoden übergibt. –

+0

Sehr guter Punkt. Ich hatte vergessen, wie nervig Java-Ausnahmen sind. –

+0

@Quinn Taylor: Allerdings wurden in vielen Teilen der Java-Bibliothek "optionale" Protokolle * erstellt. Zum Beispiel gibt die Schnittstelle "Collection" an, dass Methoden wie add() und remove() "optionale Operationen" sind, die UnsupportedOperationException auslösen, wenn sie diese nicht unterstützen. – newacct

5

Am besten Analog zu einem informellen Protokoll, das ich mir vorstellen kann, ist eine Schnittstelle, die auch eine Adapterklasse hat, um Implementierern zu erlauben, die Implementierung jeder Methode zu vermeiden.

public class MyClass { 

    private MyClassDelegate delegate; 

    public MyClass() { 

    } 

    // do interesting stuff 

    void setDelegate(MyClassDelegate delegate) { 
     this.delegate = delegate; 
    } 

    interface MyClassDelegate { 
     void aboutToDoSomethingAwesome(); 
     void didSomethingAwesome(); 
    } 

    class MyClassDelegateAdapter implements MyClassDelegate { 

     @Override 
     public void aboutToDoSomethingAwesome() { 
      /* do nothing */ 
     } 

     @Override 
     public void didSomethingAwesome() { 
      /* do nothing */ 
     } 
    } 
} 

Dann kann jemand kommen und implementieren nur das, was sie interessiert:

class AwesomeDelegate extends MyClassDelegateAdapter { 

    @Override 
    public void didSomethingAwesome() { 
     System.out.println("Yeah!"); 
    } 
} 

Entweder das, oder reine Reflexion calling „bekannt“ Methoden. Aber das ist verrückt.

+0

Aber wie immer beschränkt der Adapteransatz die Vererbung künstlich. Java hat keine optionalen Schnittstellenmethoden wie Obj-C in Protokollen, so dass Sie nichts genau das gleiche bekommen. Am saubersten (aber immer noch ärgerlich) ist es, leere Methoden zu implementieren. –