Per creare sottoclasse o per eseguire l'implementazione "predefinita"?

3

Sto creando un set di strumenti in Java composto da molti strumenti di Natural Language Processing (NLP), come Tokenizer, tagger POS, Lemmatizer, analisi delle frasi, ecc.

Voglio mettere tutti gli strumenti validi e pubblicamente disponibili nel mio set di strumenti, tutti condividendo la stessa interfaccia, in modo da poter cambiare facilmente la libreria senza modificare l'API.

La struttura del progetto è simile a questa:

tokenizer/
    Tokenizer.java (an interface)
    LibraryFooTokenizer.java (from first library)
    LibraryBarTokenizer.java (from second library)
    DefaultTokenizer.java (a default tokenizer)
lemmatizer/
    Lemmatizer.java
    LibraryFooLemmatizer.java
    LibraryBarLemmatizer.java
    DefaultLemmatizer.java

Il Library*Tokenizer.java sta implementando Tokenizer.java , quindi posso utilizzare direttamente Tokenizer nelle mie applicazioni senza dover conoscere i dettagli di implementazione. Fin qui tutto bene.

La mia domanda riguarda l'implementazione di DefaultTokenizer.java . Dovrebbe essere per qualcuno che vuole semplicemente usare un Tokenizer, che forse non sa dalla lista dei Tokenizer disponibili quale è il migliore.

Dovrebbe essere una sottoclasse della libreria attualmente migliore, o dovrebbe implementare l'interfaccia e avere la migliore libreria come membro, oppure?

Attualmente utilizzo il secondo approccio come segue:

package tokenizer;

public class DefaultTokenizer implements Tokenizer{
    public Tokenizer tokenizer;

    public DefaultTokenizer(){
        tokenizer = new LibraryFooTokenizer();
    }

    @Override
    public String[] tokenizer(String text){
        return tokenizer.tokenize(text);
    }
}

È meglio di questo?

package tokenizer;

public class DefaultTokenizer extends LibraryFooTokenizer{}

I punti che sto considerando sono la facilità di modifica, nel caso in cui in seguito LibraryBarTokenizer viene aggiornato e diventa migliore, voglio aggiornarlo.

Puoi anche rivedere questo in termini di facilità di debugging, è bello con IDE comune (attualmente sto usando Eclipse)?

Attualmente sto usando questo come set di strumenti di ricerca interni, per confrontare varie implementazioni. Esistono alcuni casi d'uso che potrebbero richiedere DefaultTokenizer:

  1. A volte potremmo decidere di voler cambiare la libreria per Tokenizer (un aggiornamento lo rende migliore, il cambio di licenza lo rende (in) appropriato). Ora, per i codici che non si preoccupano di quale Tokenizer viene utilizzato, ma che richiede solo il migliore, l'uso di Default rende più semplice il cambiamento, poiché è sufficiente cambiare la libreria predefinita.

  2. Potrebbe accadere che qualcuno possa provare a utilizzare un componente Tokenizer durante lo sviluppo di un nuovo componente. Mentre è vero che nella ricerca si suppone di conoscere i dettagli di ogni componente, a volte vogliamo testare qualcosa per cui il Tokenizer non è così cruciale per il risultato, ma è necessario. In tal caso, prendere il valore predefinito potrebbe essere buono (e se il risultato cambia se il Tokenizer è cambiato, allora c'è qualcosa di sbagliato nel modo in cui viene utilizzato il Tokenizer).

Queste ragioni potrebbero non essere troppo convincenti, ma abbiamo deciso di utilizzare tale implementazione predefinita e attualmente sto cercando dei modi per migliorare il modo in cui implementiamo l'impostazione predefinita, quindi questa domanda.

Potrebbe essere il caso in cui l'utilizzo di default è che è negativo per ragioni che non ho considerato. In tal caso, anche questo si qualifica come risposta.

Ma sarò lieto se qualcuno possa offrire ulteriori informazioni su come migliorarlo, o se non importa molto quale sia il mio modo di scegliere.

    
posta justhalf 16.09.2014 - 06:25
fonte

2 risposte

6

Manca l'opzione 3 (e altro).

Non hai un DefaultTokenizer.

Non c'è valore e induce le persone a fare delle scelte che riducano la manutenibilità in un secondo momento. Inoltre, il problema può essere risolto con una documentazione ragionevole.

Motivi per non avere il valore predefinito:

  1. in realtà non puoi cambiarlo più tardi, perché le persone si aspettano che il valore predefinito sia una rappresentazione specifica, e usano solo l'impostazione predefinita perché è conveniente.
  2. dovrai mantenere continuamente l'opzione 1, in cui avvolgi l'istanza.
  3. hai una brutta implementazione vuota per l'opzione 2.

Quindi, non farlo. Vedi una classe 'DefaultList' o una classe 'DefaultMap'? Vede un 'DefaultInputStream' o 'DefaultNumber'? n.

Le implementazioni di classe predefinite sono cattive. Consenti alle persone di leggere il tuo JavaDoc e di indicare quale si prevede che venga utilizzato in casi comuni. Se le differenze non fossero importanti, allora non avresti le due implementazioni, quindi documenta le differenze e spiega quando usare quale.

    
risposta data 16.09.2014 - 06:35
fonte
1

L'approccio alla composizione (incluso un altro Tokenizer in DefaultTokenizer) sarà più facile da modificare in futuro. Se possibile, non dovresti permettere ai Tokenizer della Biblioteca * di essere sottoclassabili. Detto questo, finché il resto del codice può fare riferimento solo a DefaultTokenizer attraverso l'interfaccia Tokenizer sarai in grado di cambiare il modo in cui è implementato senza rompere il codice di nessuno.

    
risposta data 16.09.2014 - 13:41
fonte

Leggi altre domande sui tag