2009-07-30 5 views

risposta

8

La risposta breve è che non c'è niente in Java il più vicino possibile, ma ci sono alternative. Lo schema dei delegati non è difficile da implementare, non è proprio così conveniente come farlo con Objective-C.

La ragione di lavoro "protocolli informali" in Objective-C è perché il linguaggio supporta categorie, che permettono di aggiungere metodi alle classi esistenti senza sottoclasse, o addirittura avere accesso al codice sorgente. Pertanto, molti protocolli informali sono una categoria su NSObject. Questo è chiaramente impossibile in Java.

Objective-C 2.0 opta per i metodi di protocollo @optional, che è un'astrazione molto più pulita e preferita per il nuovo codice, ma anche per avere un equivalente in Java.

Onestamente, l'approccio più flessibile consiste nel definire un protocollo delegato, quindi le classi implementano tutti i metodi. (Con IDE moderni come Eclipse, questo è banale.) Molte interfacce Java hanno una classe adattatore associata, e questo è un approccio comune per non richiedere all'utente di implementare molti metodi vuoti, ma limita l'ereditarietà, il che rende inflessibile la progettazione del codice . (Josh Bloch affronta questo nel suo libro "Efficace Java".) Il mio suggerimento sarebbe di fornire solo un'interfaccia prima, quindi aggiungere un adattatore se è veramente necessario.

Qualsiasi cosa tu faccia, evitare di lanciare un UnsupportedOperationException per metodi "non implementati". Ciò impone alla classe delegante di gestire le eccezioni per i metodi che dovrebbero essere facoltativi. L'approccio corretto consiste nell'implementare un metodo che non fa nulla, restituisce un valore predefinito, ecc. Questi valori dovrebbero essere ben documentati per i metodi che non hanno un tipo di reso vuoto.

2

Non c'è niente che ti impedisca di utilizzare il pattern delegato nei tuoi oggetti Java (non è un pattern comunemente usato nel JDK come in Cocoa). Basta avere un ivar delegate di un tipo conforme all'interfaccia WhateverDelegate, quindi nei metodi di istanza che si desidera delegare, inoltrare la chiamata al metodo sull'oggetto delegato, se esiste. Probabilmente ti ritroverai con qualcosa che somiglia molto a this, tranne in Java invece di Obj-C.

Per quanto riguarda le interfacce opzionali, sarebbe più difficile. Suggerirei di dichiarare l'interfaccia, dichiarando una classe astratta che implementa metodi facoltativi come metodi vuoti e quindi sottoclassi la classe astratta, sovrascrivendo i metodi facoltativi che si desidera implementare con questo particolare oggetto. C'è una limitazione potenzialmente grave qui a causa della mancanza di ereditarietà multipla in Java, ma è il più vicino possibile.

+4

Lanciare "UnsupportedOperationException' è una pessima idea! Le API non dovrebbero mai obbligare gli utenti ad affrontare le eccezioni nei normali schemi di utilizzo, solo per un flusso eccezionale. Lo schema dei delegati in Cocoa è solido in quanto passa silenziosamente a metodi di delegati non implementati. –

+0

Ottimo punto. Avevo dimenticato quanto fastidiose sono le eccezioni Java. –

+0

@Quinn Taylor: tuttavia, è così che i "protocolli" opzionali * sono stati * fatti in molte parti della libreria Java. Ad esempio, l'interfaccia "Collection" specifica che metodi come add() e remove() sono "operazioni opzionali", che lancia UnsupportedOperationException se non lo supportano. – newacct

5

Il migliore analogo a un protocollo informale a cui posso pensare è un'interfaccia che ha anche una classe adattatore per consentire agli implementatori di evitare l'implementazione di ogni metodo.

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 */ 
     } 
    } 
} 

Poi qualcuno può venire avanti e solo implementare le cose che si preoccupano:

class AwesomeDelegate extends MyClassDelegateAdapter { 

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

O che, o vocazione pura riflessione "conosciuto" metodi. Ma è folle.

+0

Ma come sempre, l'approccio dell'adattatore limita artificialmente l'ereditarietà. Java non ha metodi di interfaccia opzionali come Obj-C fa nei protocolli, quindi non otterrai qualcosa esattamente uguale. L'approccio più pulito (ma ancora fastidioso) consiste nell'implementare metodi vuoti. –