Progettazione API: approccio concreto vs. approccio astratto - buone pratiche?


25

Quando discutiamo di API tra sistemi (a livello aziendale) ci sono spesso due diversi punti di vista nel nostro team: alcune persone preferiscono un approccio più generico - diciamo - generico astratto, altri un approccio "concreto" diretto.

Esempio: la progettazione di una semplice API "ricerca persona". la versione concreta sarebbe

 searchPerson(String name, boolean soundEx,
              String firstName, boolean soundEx,
              String dateOfBirth)

Le persone a favore della versione concreta dicono:

  • l'API è auto-documentata
  • è facile da capire
  • è facile da validare (compilatore o come servizio web: validazione dello schema)
  • BACIO

L'altro gruppo di persone nel nostro team direbbe "Questo è solo un elenco di criteri di ricerca"

searchPerson(List<SearchCriteria> criteria)

con

SearchCritera {
  String parameter,
  String value,
  Map<String, String> options
}

con eventualmente la creazione di "parametro" di un certo tipo di enumerazione.

I sostenitori dicono:

  • senza modificare la (dichiarazione di) API, l'implementazione può cambiare, ad esempio aggiungendo più criteri o più opzioni. Anche senza sincronizzare tale modifica al momento della distribuzione.
  • la documentazione è necessaria anche con la variante concreta
  • la convalida dello schema è sopravvalutata, spesso è necessario convalidare ulteriormente, lo schema non può gestire tutti i casi
  • abbiamo già un'API simile con un altro sistema: il riutilizzo

La contro argomentazione è

  • molta documentazione su parametri validi e combinazioni di parametri validi
  • più sforzo comunicativo perché è più difficile da capire per gli altri team

Ci sono delle migliori pratiche? Letteratura?


3
ripetuto "String nome / nome, suono booleanoEx" è una chiara violazione di dry e suggerisce che questo progetto non ha risolto il fatto che il nome dovrebbe andare con soundEx. Di fronte a semplici errori di progettazione del genere, è difficile procedere con analisi più sofisticate
moscerino

Il contrario di "calcestruzzo" non è "generico", è "astratto". L'astrazione è molto importante per una libreria o API, e questa discussione non riesce a porre la domanda veramente fondamentale, fissando invece su quella che è francamente una domanda piuttosto banale di stile. FWIW, i contro-argomenti per l'opzione B sembrano un carico di FUD, non dovresti aver bisogno di ulteriore documentazione o comunicazione se il design dell'API è anche mezzo pulito e segue i principi SOLIDI.
Aaronaught,

@Aaronaught grazie per averlo sottolineato ("astratto"). Potrebbe essere un problema di traduzione, "generisch" in tedesco suona ancora bene per me. Qual è la "domanda veramente fondamentale" per te?
erik,

4
@Aaronaught: la domanda non riguarda l'astratto. La correzione corretta sarebbe quella opposta di "generico" è "specifico", non "concreto".
Jan Hudec,

Un altro voto per questo non riguarda il generico rispetto all'astratto, ma il generico rispetto allo specifico. L'esempio "concreto" sopra è in realtà specifico per il nome, firstName e dateOfBirth, l'altro esempio è generico per qualsiasi parametro. Né sono particolarmente astratti. Modificherei il titolo ma non voglio iniziare una guerra di modifica :-)
Modificherei

Risposte:


18

Dipende da quanti campi stai parlando e da come vengono utilizzati. Il calcestruzzo è preferibile per le query altamente strutturate con solo pochi campi, ma se l'interrogazione tende a essere molto libera, l'approccio concreto diventa rapidamente ingombrante con più di tre o quattro campi.

D'altra parte, è molto difficile mantenere pura un'API generica. Se fai una semplice ricerca per nome in molti posti, alla fine qualcuno si stancherà di ripetere le stesse cinque righe di codice e lo avvolgerà in una funzione. Una tale API si evolve invariabilmente in un ibrido di una query generica insieme a wrapper concreti per le query più comunemente utilizzate. E non vedo nulla di sbagliato in questo. Ti dà il meglio di entrambi i mondi.


7

Progettare una buona API è un'arte. Una buona API è apprezzata anche dopo il passare del tempo. Secondo me, non ci dovrebbero essere pregiudizi generali sulla linea astratta-concreta. Alcuni parametri possono essere concreti come i giorni della settimana, alcuni richiedono di essere progettati per l'estensibilità (ed è abbastanza stupido renderli concreti, ad esempio, parte dei nomi delle funzioni), ma un altro può andare ancora oltre e per avere un aspetto elegante L'API uno deve fornire callback o persino un linguaggio specifico del dominio aiuterà a combattere la complessità.

Raramente accadono cose nuove sotto la Luna. Dai un'occhiata alla tecnica nota, in particolare standard e formati stabiliti (ad esempio, molte cose possono essere modellate dopo i feed, le descrizioni degli eventi sono state elaborate in ical / vcal). Rendi la tua API facilmente additiva, dove entità frequenti e onnipresenti sono concrete e le estensioni previste sono dizionari. Ci sono anche alcuni modelli ben consolidati per affrontare situazioni specifiche. Ad esempio, la gestione della richiesta HTTP (e simili) può essere modellata nell'API con oggetti Request e Response.

Prima di progettare l'API, fai un brainstorming su aspetti, compresi quelli, che non saranno inclusi, ma devi essere consapevole. Esempi di questo sono lingua, direzione della scrittura, codifica, impostazioni locali, informazioni sul fuso orario e simili. Presta attenzione ai luoghi in cui possono apparire i multipli: usa l'elenco, non un singolo valore per loro. Ad esempio, se stai progettando un'API per il sistema di videochat, la tua API sarà molto più utile, se supponi N partecipanti, non solo due (anche se le tue specifiche al momento sono tali).

A volte, essere astratti aiuta a ridurre drasticamente la complessità: anche se si progetta una calcolatrice per aggiungere solo 3 + 4, 2 + 2 e 7 + 6, può essere molto più semplice implementare X + Y (con limiti tecnicamente fattibili su X e Y e includi ADD (X, Y) nella tua API anziché ADD_3_4 (), ADD_2_2 (), ...

Tutto sommato, scegliere in un modo o nell'altro è solo un dettaglio tecnico. La documentazione deve descrivere i casi di uso frequente in modo concreto.

Qualunque cosa tu faccia dal lato della struttura dei dati, fornisci un campo per una versione dell'API.

Per riassumere, l'API dovrebbe ridurre al minimo la complessità quando si ha a che fare con il proprio software. PER apprezzare l'API, il livello di complessità esposta dovrebbe essere adeguato. La decisione sulla forma dell'API dipende molto dalla stabilità del dominio problematico. Pertanto, ci dovrebbe essere una stima su quale direzione cresceranno il software e la sua API, perché queste informazioni possono influenzare l'equazione per la complessità. Inoltre, desing API è lì per le persone a capire. Se ci sono buone tradizioni nell'area tecnologica del software in cui ti trovi, cerca di non discostarti molto da esse, poiché ti aiuterà a capire. Prendi in considerazione per chi scrivi. Gli utenti più avanzati apprezzeranno la generalità e la flessibilità, mentre quelli con meno esperienza potrebbero sentirsi più a proprio agio con i concreti. Tuttavia, prenditi cura della maggior parte degli utenti API lì,

Per quanto riguarda la letteratura, posso raccomandare ai programmatori leader di "Beautiful Code" di spiegare come pensano di Andy Oram, Greg Wilson, poiché penso che la bellezza sia percepire l'ottimalità nascosta (e l'idoneità per uno scopo).


1

La mia preferenza personale è quella di essere astratta, eppure le politiche della mia azienda mi bloccano per essere concreti. Questa è la fine del dibattito per me :)

Hai fatto un buon lavoro elencando pro e contro per entrambi gli approcci e se continui a scavare troverai molti argomenti a favore di entrambe le parti. Fintanto che l'architettura della tua API è sviluppata correttamente, ovvero hai pensato a come verrà utilizzata oggi e come potrebbe evolversi e crescere in futuro, allora dovresti andare bene in entrambi i casi.

Ecco due segnalibri che ho avuto con punti di vista opposti:

Favorire le classi astratte

Favorire le interfacce

Chiediti: "L'API soddisfa i requisiti della mia azienda? Ho criteri ben definiti per il successo? Può ridimensionare?". Sembrano seguire le migliori pratiche davvero semplici da seguire, ma onestamente sono molto più importanti del concreto rispetto al generico.


1

Non direi che un'API astratta sia necessariamente più difficile da convalidare. Se i parametri dei criteri sono abbastanza semplici e hanno piccole dipendenze tra loro, non fa molta differenza se si passano i parametri separatamente o in un array. Devi ancora convalidarli tutti. Ma ciò dipende dalla progettazione dei parametri dei criteri e degli oggetti stessi.

Se l'API è abbastanza complessa, avere metodi concreti non è un'opzione. Ad un certo punto probabilmente finirai con entrambi i metodi con troppi parametri o metodi troppo semplici che non copriranno tutti i casi d'uso richiesti. Per quanto riguarda la mia esperienza personale nella progettazione di API che consumano, è meglio avere metodi più generici a livello di API e implementare wrapper richiesti specifici a livello di applicazione.


1

L'argomento modifica dovrebbe essere respinto con YAGNI. Fondamentalmente a meno che tu non abbia effettivamente almeno 3 diversi casi d'uso che utilizzano l'API generica in modo diverso, le probabilità sono piuttosto basse di progettarlo in modo che non debba cambiare quando si presenta il prossimo caso d'uso (e quando hai l'uso- casi, ovviamente hai bisogno dell'interfaccia generica, punto). Quindi non tentare ed essere pronti per il cambiamento.

In entrambi i casi non è necessario sincronizzare la modifica per la distribuzione. Quando si generalizza l'interfaccia in un secondo momento, è sempre possibile fornire l'interfaccia più specifica per la compatibilità con le versioni precedenti. Ma in pratica qualsiasi distribuzione avrà così tante modifiche che la sincronizzerai comunque in modo da non dover testare gli stati intermedi. Non lo vedrei neanche come argomento.

Per quanto riguarda la documentazione, entrambe le soluzioni possono essere facili da usare e ovvie. Ma è un argomento importante. Implementa l'interfaccia in modo che sia facile da usare nei tuoi casi reali. A volte specifici possono essere migliori e a volte generici.


1

Preferirei l'approccio dell'interfaccia astratta. Inserire una query per quel tipo di servizio (ricerca) è un problema comune e probabilmente si verificherà di nuovo. Inoltre, troverete probabilmente più candidati al servizio idonei a riutilizzare un'interfaccia più generale. Per essere in grado di fornire un'interfaccia comune coerente per tali servizi, non enumererei i parametri di query attualmente identificati nella definizione dell'interfaccia.

Come è stato sottolineato in precedenza, mi piace l'opportunità di cambiare o estendere l'implementazione senza modificare l'interfaccia. L'aggiunta di un altro criterio di ricerca non deve riflettersi nella definizione del servizio.

Sebbene non sia una questione progettare interfacce ben definite, concise ed espresse, dovrete sempre fornire anche della documentazione. L'aggiunta dell'ambito di definizione per criteri di ricerca validi non è un tale onere.


1

Il miglior riassunto che io abbia mai visto è la scala di Rusty, ora chiamato il manifesto del design API di Rusty . Posso solo consigliare vivamente quello. Per completezza, cito il riassunto della scala dal primo collegamento (meglio in alto, più in basso):

Buone API

  • È impossibile sbagliarsi.
  • Il compilatore / linker non ti farà sbagliare.
  • Il compilatore avviserà se si sbaglia.
  • L'uso ovvio è (probabilmente) quello corretto.
  • Il nome ti dice come usarlo.
  • Fallo bene o si romperà sempre in fase di esecuzione.
  • Segui la convenzione comune e avrai capito bene.
  • Leggi la documentazione e la otterrai correttamente.
  • Leggi l'implementazione e avrai capito bene.
  • Leggi il thread della mailing list corretto e lo otterrai correttamente.

API non valide

  • Leggi il thread della mailing list e sbaglierai.
  • Leggi l'implementazione e sbaglierai.
  • Leggi la documentazione e la sbaglierai.
  • Segui la convenzione comune e sbaglierai.
  • Fallo nel modo giusto e a volte si romperà in fase di esecuzione.
  • Il nome ti dice come non usarlo.
  • L'uso ovvio è sbagliato.
  • Il compilatore avviserà se lo capisci bene.
  • Il compilatore / linker non ti permetterà di farlo bene.
  • È impossibile avere ragione.

Entrambe le pagine dei dettagli qui e qui vengono con una discussione approfondita di ogni punto. È davvero una lettura obbligata per i progettisti di API. Grazie Rusty, se mai hai letto questo.


0

Nelle parole di laici:

  • L'approccio astratto ha il vantaggio di consentire di costruire metodi concreti attorno ad esso.
  • Il contrario non è vero

UDP ha il vantaggio di consentirti di creare i tuoi flussi affidabili. Allora perché quasi tutti usano TCP?
svick,

C'è anche la considerazione della maggior parte dei casi d'uso. Alcuni casi possono essere necessari così frequentemente, che è possibile renderli speciali.
Roman Susi,

0

Se estendi SearchCriteriaun po ' l' idea, può darti flessibilità come la creazione di criteri AND, ORecc. Se hai bisogno di tale funzionalità, questo sarebbe l'approccio migliore.

Altrimenti, progettalo per usabilità. Rendi l'API semplice per le persone che la usano. Se hai alcune funzioni di base che sono spesso necessarie (come cercare una persona per nome), forniscile direttamente. Se gli utenti avanzati necessitano di ricerche avanzate, possono comunque utilizzare il SearchCriteria.


0

Cosa sta facendo il codice dietro l'API? Se è qualcosa di flessibile, allora un'API flessibile è buona. Se il codice dietro l'API è molto specifico, allora dare una faccia flessibile su di esso significa solo che gli utenti dell'API saranno frustrati e infastiditi da tutto ciò che l'API pretende sia possibile, ma che in realtà non può essere realizzato.

Per la tua persona esempio di ricerca sono obbligatori tutti e tre i campi? In tal caso, l'elenco dei criteri è errato perché consente una moltitudine di usi che semplicemente non funzionano. Se non è quindi necessario che l'utente specifichi gli input non richiesti è male. Con quale probabilità verrà aggiunta la ricerca per indirizzo in V2? L'interfaccia flessibile semplifica l'aggiunta rispetto a quella non flessibile.

Non tutti i sistemi devono essere ultra flessibili, cercando di rendere tutto così come Architecture Astronauting. Un arco flessibile lancia frecce. Una spada flessibile è utile come un pollo di gomma.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.