Che senso ha che ogni classe di servizio abbia un'interfaccia? [duplicare]

84

Presso l'azienda a cui lavoro, ogni classe di servizio ha un'interfaccia corrispondente. È necessariamente? La maggior parte di queste interfacce viene utilizzata solo da una singola classe e non stiamo creando alcun tipo di API pubblica. Con moderne librerie di mocking in grado di simulare classi e IDE concreti in grado di estrarre un'interfaccia da una classe con due o così tanti clic, è solo un retaggio di epoche precedenti?

    
posta Bob Roberts 24.05.2012 - 17:53
fonte

6 risposte

79

La tua azienda segue i principi SOLID e punta a un'interfaccia anziché a una classe concreta che aggiunge zero costi generali al programma. Quello che stanno facendo è una quantità molto modesta di lavoro extra che può ripagare i volumi quando le supposizioni che si stanno facendo finiscono per essere false ... e non si può mai dire quale sarà il presupposto. Quello che stai raccomandando mentre lavori leggermente meno, può costare molte, molte, molte ore di refactoring che potrebbero essere state fatte in anticipo semplicemente seguendo i principi di progettazione OO di base.

Dovresti imparare da questi ragazzi e considerarti fortunato. Vorrei lavorare in un ambiente in cui il design solido era un obiettivo prioritario.

    
risposta data 24.05.2012 - 17:59
fonte
66

Data la tua domanda, suppongo che i motivi per questo tipo di design non siano documentati.

L'uso ingiustificato di un'interfaccia con singola implementazione è errato in quanto ciò viola YAGNI . Nella mia esperienza, anche questo è stato piuttosto dannoso dal punto di vista della manutenzione, soprattutto perché i metodi che implementano l'interfaccia sono costretti a essere inutilmente pubblici. Dopo aver raccolto più esperienza refactoring , probabilmente imparerai ad apprezzare il fascino modesto di private e pacchetto privato modificatori di accesso che consentono di focalizzare l'attenzione all'interno di una singola classe / pacchetto.

Per quanto riguarda l'uso giustificato non documentato di questo idioma, è nei miei occhi un aspetto negativo, innanzitutto perché non consente a un manutentore di "distinguere il buono dal cattivo". Di conseguenza, questo lascia uno tra opzioni non abbastanza allettanti. Le opzioni sono, o mantieni le cose pubbliche discutibili così com'è, quindi ripetutamente diminuendo la produttività ogni volta che hai bisogno di cambiare il codice, oppure apporti rischiose modificando ciecamente "collassando" l'implementazione singola in modo più semplice per mantenere POJO - esponendo il sé al rischio di scoprire dolorosamente che questo è un modo sbagliato, che qualche altro (Dio non voglia, remoto ) modulo fuori dalla tua vista si aspetta l'interfaccia.

Ho riscontrato "rivelazioni" causate da un errato collasso dell'interfaccia di implementazione singola poche volte. Ogni volta è stata un'esperienza piuttosto educativa, ma non vorrei davvero tornarci. Il fatto è che questo accade in fase di test, di integrazione o persino di accettazione da parte degli utenti e il rollback / correzione dell'errore fatto molto tempo fa in questa fase è piuttosto ingombrante.

  • Un'opzione che non dovrebbe essere lasciata senza menzionare è quella di indagare se l'uso non documentato è giustificato (e, successivamente, collassare o documentare rispettivamente) proprio nel momento in cui lo trovi.

    Per me, questo è stato abbastanza controproducente. Questo approccio obbliga essenzialmente a eseguire test di integrazione completi, che è probabilmente uno dei modi più efficaci per interrompe un flusso nel mezzo di alcune complicate correzioni / refactoring.

I just couldn't see the practicality... the interfaces are used one-to-one like com.company.service.foo and com.company.service.foo.impl

Ben di sotto si tratta di come gestisco tipicamente casi del genere.

  1. Crea un ticket in tracker dei problemi , come " PRJ-123 non documentato uso discutibile dell'interfaccia con singola implementazione in Foo / FooImpl ".
    Aggiungi alla descrizione del ticket o commenta una spiegazione che danneggia la manutenibilità e fai notare che senza documentazione questo sembra overengineering . Aggiungi un commento che suggerisca di risolvere il problema "collassando" questo in un POJO, per una più facile manutenibilità.
  2. Aspetta un po 'di tempo nel caso in cui qualcuno venga a spiegare le cose. Aspetterei almeno una settimana o due

    • Se qualcuno spiega lo scopo, a) metti quello al ticket, b) aggiungi a Foo javadocs qualcosa come scopo dell'interfaccia spiegato nel ticket PRJ-123 , c) chiudi il ticket, d) salta i prossimi tre passaggi.
    • Altrimenti, ...
  3. Vieni al caposquadra o al responsabile per chiedere cosa penserebbero se aggiusto il biglietto come suggerito.
    Scegli un consulente che abbia autorità sufficiente per sostenere il "test dell'urlo" nel caso in cui la soluzione suggerita risulti errata .
  4. Collabora a POJO come da soluzione suggerita, disponi riesame del codice se è possibile (in casi scivolosi come quello non ti farà male coprirti la schiena).
  5. Attendi fino a quando la modifica non supera il test completo e aggiorna il ticket a seconda del suo esito; aggiungi le informazioni sul fatto che la modifica abbia superato il test o meno.
  6. Crea e gestisci un nuovo ticket di monitoraggio dei problemi per gestire i casi simili rimanenti in base al risultato del tuo ticket pilota ( PRJ-123 ): o documenta lo scopo, oppure comprimi in POJO.

    
risposta data 24.05.2012 - 23:18
fonte
31

Ecco come Adam Bien pensa a questa domanda: Servizio s = nuovo ServiceImpl () - Perché lo stai facendo?

That is not only bloat (and the opposite of "Convention Over Configuration" idea), but it causes real damage:

  1. Imagine you get another implementation (thats the whole point of an interface) - how would you name it?
  2. It doubles the amount of artifacts - and significantly increases the "complexity"
  3. It is really funny to read the javadocs on the interface and the impl - another redundancy
  4. The navigation in the IDE is less fluent

e sono d'accordo con lui.

Forse i tuoi colleghi sono ancora bloccati in tempo con J2EE, perché questo era necessario quando si lavorava con J2EE, forse puoi trovare su Internet vecchi tutorial sui tempi duri in J2EE.

    
risposta data 03.09.2012 - 19:08
fonte
17

Le interfacce Java non riguardano solo la mockability (anche se, ovviamente, questo è un fattore). Un'interfaccia espone un contratto pubblico per il tuo servizio, senza dettagli di implementazione come metodi o attributi privati.

Tieni presente che "pubblico" quando si utilizzano i servizi interni (come nel tuo caso) si riferisce agli utenti effettivi di tali servizi, inclusi - ma non limitati a - altri programmatori e servizi interni!

    
risposta data 24.05.2012 - 18:11
fonte
10

Una possibile ragione per cui ogni classe di servizio ha un'interfaccia è che rende molto più semplici le interazioni con framework sofisticati (ad esempio, Spring, JEE). Questo perché Java può semplicemente generare oggetti interceptor contro le interfacce (vedere java.lang.reflect.Proxy ); farlo contro classi concrete è molto più complicato e pieno di alcuni avvertimenti non ovvi quando si lavora con più diversi classloader. Questo è doppiamente vero se lavori con OSGi, dove queste interfacce di servizio finiranno per essere caricate da un altro classloader dalle implementazioni di quei servizi. Quello (insieme al tessuto di scoperta dei servizi) a sua volta impone una separazione molto strong tra ogni servizio e i suoi clienti; i clienti letteralmente non possono interrompere l'astrazione.

Tieni presente che anche se un IDE è in grado di estrarre un'interfaccia da una classe con un solo clic, ciò non significa che estrarrà necessariamente l'interfaccia corretta . L'intelligenza è ancora necessaria.

    
risposta data 26.05.2012 - 16:39
fonte
0

Sembra che l'overhead utilizzi inizialmente Interface .. ma in seguito, quando avremo bisogno di estendere alcune funzionalità, queste interfacce aiutano molto.

Se fai solo un'implementazione concreta, devi cambiare molto l'implementazione del codice. Ma usando l'interfaccia devi solo aggiungere una nuova classe che implementa il contratto di Interface (e puoi chiamare un nuovo oggetto di classe dalla variabile di riferimento di Vecchia interfaccia).

    
risposta data 26.05.2012 - 11:28
fonte

Leggi altre domande sui tag