Evoluzione di un'interfaccia che non dovrebbe essere implementata dal client

5

Sto per scrivere una libreria Java. Fondamentalmente, questa libreria fornisce qualcosa di simile al suo utente:

interface Foo {
  void doA();
  boolean aWorked();
  void doB(int value);
}

L'utente non dovrebbe implementare questa interfaccia (ovviamente). Pertanto, il codice utente sarà simile a questo:

Foo f = Library::SomeFactory();
if (someDecison()) {
  f.doA();
  if (!f.aWorked()) {
    f.doB(21);
  }
} else {
  f.doB(42);
}

Posso assicurarmi che né le precondizioni né le post-condizioni dei metodi nell'interfaccia cambiano in futuro. Ma potrebbe esserci un nuovo metodo, ad esempio doC() .

Ora, varie risorse tra cui Documenti di Orcale suggeriscono che è sufficiente aggiungere questo doC() all'interfaccia precedente è una cattiva idea. Propongono diverse soluzioni estendendo l'interfaccia ...

interface Foo2 : extends Foo {
  void doC();
}

... per far saltare il codice con schemi di comando e cosa no. Ma la ragione di supporto è sempre che "tutte le classi di implementazione devono essere cambiate". Nel mio caso questo non è un problema, poiché tutte le classi di implementazione sono "sotto il mio controllo" e dovrà essere modificato in entrambi i modi (quando c'è un doC() ).

Semplicemente aggiungere il metodo a Foo è davvero una cattiva idea? E se sì, perché? C'è qualcosa che non sto prendendo in considerazione qui? Il mio obiettivo principale è quello di non violare alcun codice utente scritto contro tale interfaccia.

// That's what I'm planning
interface Foo {
  void doA();
  boolean aWorked();
  void doB(int value);
  void doC();
}

Questa fonte supporta la mia sensazione che possa essere così semplice:

[..] If the method is added to a class (interface) which Clients are not allowed to subclass (to implement), then it is not a breaking change. [..]

    
posta Daniel Jour 27.10.2016 - 01:17
fonte

4 risposte

4

Non esiste un meccanismo Java per impedire ai client di implementare un'interfaccia accessibile a loro. Il testo che stai citando dice "non è permesso implementare". Purtroppo, l'unico modo che può essere vero (applicato) è se l'interfaccia è inaccessibile al client.

Ci sono ragioni per cui un cliente potrebbe voler implementare quell'interfaccia; ad esempio, se stanno cercando di riunire diverse librerie diverse e trovano che la tua interfaccia sia una buona astrazione da riutilizzare.

Puoi mettere in guardia i tuoi clienti contro l'implementazione della tua interfaccia - che la tua intenzione è di estenderla, oppure, puoi usare i nuovi metodi predefiniti di Java quando estendi l'interfaccia (o entrambi).

    
risposta data 27.10.2016 - 01:37
fonte
3

Le buone pratiche sono belle e dandy, ma è importante essere pragmatici.

In questo caso, non vedo nulla di sbagliato nell'estendere la tua interfaccia sul posto in futuro. Come hai detto tu, le uniche classi interessate sono sotto il tuo controllo.

Inoltre, perché hai un membro aWorked() se puoi semplicemente avere doA() restituire un risultato positivo / negativo?

    
risposta data 27.10.2016 - 01:27
fonte
1

Evoluzione di un'interfaccia senza interrompere il codice

Qualsiasi nuovo metodo dovrebbe fornire un comportamento predefinito , che è più facile (e più pulito ) alternativa alla definizione di un nuovo sottotipo.

Perché l'aggiunta di un nuovo metodo astratto a un'interfaccia non è corretta?

Fa esattamente quello che vuoi evitare: codice di interruzione. Se i client utilizzano una versione più recente del framework per correzioni di bug, saranno costretti a dichiarare il nuovo metodo in qualunque tipo implementare l'interfaccia.

Impedire ai client di implementare la tua interfaccia

Anche se Java lo supporta, cosa che non fa, vorrei sconsigliarlo. Limiteresti l'utilizzo del tuo framework.

Se il tuo framework manca di un'implementazione che il client potrebbe richiedere / desidera, essi (o chiunque altro, magari uno sviluppatore più esperto) dovrebbero essere in grado di estendere Foo per mostrare nuove utili implementazioni che potresti non avere avuto il tempo scrivere, o forse nemmeno pensarci.

    
risposta data 02.11.2016 - 03:16
fonte
0

Se l'interfaccia non dovrebbe essere implementata dal client, dovresti renderla pacchetto-privato in modo che i client non sappiano nemmeno che esiste. Se definisce un tipo che fa parte della tua API pubblica, potresti renderlo una superclasse delle tue implementazioni. La superclasse potrebbe essere abstract o avere un costruttore pacchetto-privato e le implementazioni potrebbero essere chiuse all'estensione dichiarandole final o limitando l'accesso ai loro costruttori.

Puoi sempre rimuovere queste restrizioni in seguito se scopri un motivo valido per farlo. Ma non è possibile aggiungerli in seguito senza rischiare la rottura nel codice di terze parti. Quindi è una buona pratica in un'API pubblica limitare la visibilità e l'estensibilità di tutto il più possibile e aprirla più tardi.

    
risposta data 04.11.2016 - 03:38
fonte

Leggi altre domande sui tag