Suddividi interfacce di grandi dimensioni

9

Sto usando una grande interfaccia con circa 50 metodi per accedere a un database. L'interfaccia è stata scritta da un mio collega. Ne abbiamo discusso:

Io: 50 metodi è troppo. È un odore di codice.
Collega: cosa dovrei fare al riguardo? Vuoi l'accesso al DB: ce l'hai.
Io: Sì, ma non è chiaro e difficilmente mantenibile in futuro.
Collega: OK, hai ragione, non è bello. Come dovrebbe essere l'interfaccia allora?
Io: che ne dici di 5 metodi che restituiscono oggetti che hanno, come, 10 metodi ciascuno?

Mmmh, ma non sarebbe lo stesso? Questo porta davvero a una maggiore chiarezza? Vale la pena?

Ogni tanto mi trovo in una situazione in cui voglio un'interfaccia e la prima cosa che mi viene in mente è l'interfaccia one, big . C'è un modello di progettazione generale per questo?

Aggiornamento (risposta al commento di SJuan):

Il "tipo di metodi": è un'interfaccia per il recupero dei dati da un database. Tutti i metodi hanno la forma (pseudocodice)

List<Typename> createTablenameList()

I metodi e le tabelle non sono esattamente in una relazione 1-1, l'enfasi è più sul fatto che ottieni sempre una sorta di lista proveniente da un database.

    
posta TobiMcNamobi 18.06.2014 - 13:44
fonte

4 risposte

13

Sì, 50 metodi sono un odore di codice, ma un odore di codice significa dare una seconda occhiata a questo, non che sia automaticamente sbagliato. Se ogni cliente che utilizza quella classe potenzialmente ha bisogno di tutti i 50 metodi, potrebbe non esserci il caso di dividerlo. Tuttavia, questo è improbabile. Il mio punto è che dividere arbitrariamente un'interfaccia può essere peggio che non dividerlo affatto.

Non esiste un singolo pattern per risolverlo, ma il principio che descrive lo stato desiderato è il Principio di segregazione dell'interfaccia (il 'I' in SOLID), che afferma che nessun client dovrebbe essere costretto a dipendere da metodi che non usa.

La descrizione dell'ISP ti dà un suggerimento su come risolverlo: guarda il client . Spesso, osservando una classe, sembra che tutto funzioni insieme, ma emergono chiare divisioni quando si guardano i client che usano quella classe. Considerare sempre i client prima di progettare un'interfaccia.

L'altro modo per determinare se e dove un'interfaccia dovrebbe essere divisa è fare una seconda implementazione. Quello che spesso accade è che la tua seconda implementazione non ha bisogno di molti metodi, quindi quelli chiaramente dovrebbero essere scissi nella loro interfaccia.

    
risposta data 18.06.2014 - 21:02
fonte
12

Colleague: OK, you are right, it's not nice. How should the interface look like then?

Me: How about 5 methods that return objects that have, like, 10 methods each?

Questo non è un buon criterio (in realtà non ci sono criteri in questa affermazione). Puoi raggrupparli per (supponendo che la tua applicazione sia un'app per le transazioni finanziarie, per i miei esempi):

  • funzionalità (inserisce, aggiorna, seleziona, transazioni, metadati, schema, ecc.)
  • entità (utente DAO , deposito DAO, ecc.)
  • area applicativa (transazioni finanziarie, gestione utenti, totali, ecc.)
  • livello di astrazione (tutto il codice di accesso alla tabella è un modulo separato, tutte le API selezionate sono nella propria gerarchia, il supporto delle transazioni è separato, tutto il codice di conversione in un modulo, tutto il codice di convalida in un modulo, ecc.)

Mmmh, but wouldn't this be the same? Does this really lead to more clarity? Is it worth the effort?

Se scegli i criteri giusti, sicuramente. Se non lo fai, sicuramente no:).

Alcuni esempi:

  • guarda gli oggetti ADODB per un semplicistico esempio di primitive OO (probabilmente l'API DB lo offre già)

  • guarda il modello di dati Django ( link ) per un'idea del modello di dati con un alto livello di astrazione (in C ++ probabilmente avrai bisogno di un po 'di codice per la piastra della caldaia, ma è una buona idea). Questa implementazione è stata progettata tenendo presente un ruolo di "modello", all'interno del modello di progettazione MVC.

  • guarda l'API sqlite per un'idea di API piatta ( link ), che consiste in un semplice funzionamento primitive (API C).

risposta data 18.06.2014 - 16:38
fonte
2

Every now and then I'm in a situation where I want an interface and the first thing that comes to mind is one big interface. Is there a general design pattern for this?

È un anti-modello di design chiamato la classe monolitica . Avere 50 metodi in una classe o in un'interfaccia è una probabile violazione del SRP . La lezione monolitica nasce perché cerca di essere tutto per tutti.

DCI affligge il metodo. In sostanza, le numerose responsabilità di una classe potrebbero essere suddivise in ruoli (scaricati in altre classi) che sono rilevanti solo in determinati contesti. L'applicazione dei ruoli può essere raggiunta in vari modi, tra cui mixin o decoratori . Questo approccio mantiene le lezioni focalizzate e snelle.

How about 5 methods that return objects that have, like, 10 methods each?

Questo suggerisce di istanziare tutti i ruoli quando l'oggetto stesso viene istanziato. Ma perché istituire ruoli che potresti non aver bisogno? Invece, istanzia un ruolo nel contesto in cui ne hai effettivamente bisogno.

Se ritieni che il refactoring verso DCI non sia ovvio, puoi utilizzare un modello di visitatore più semplice. Fornisce un vantaggio simile senza enfatizzare la creazione di contesti case use.

    
risposta data 18.06.2014 - 20:28
fonte
0

I livelli di accesso ai dati tendono ad avere molti metodi collegati a una classe. Se hai mai lavorato con Entity Framework o altri strumenti ORM, vedrai che generano centinaia di metodi. Presumo che tu e il tuo collega lo stiate implementando manualmente. Non è necessario un odore di codice, ma non è bello da guardare. Senza conoscere il tuo dominio, è difficile da dire.

    
risposta data 18.06.2014 - 14:11
fonte

Leggi altre domande sui tag