2014-10-17 14 views
6

Questo problema mi viene così spesso nella mia codifica che sono stupito di poter trovare così poco riferimento ad esso e apprezzare i pensieri e le idee di altre persone.Come distinguere le interfacce "Role" dalle interfacce "Result"?

Definisco molte API, per i framework su cui lavoro e all'interno di modelli di dominio di grandi dimensioni che voglio suddividere. Queste API consistono quasi interamente di interfacce (nel mio caso, le interfacce C#). Trovo, più e più volte, che voglio distinguere tra due tipi di interfaccia. In assenza di trovare le condizioni più diffusi, definisco questi due come segue:

  • interfacce 'ruolo' sono destinati ad essere implementata da oggetti esterni delle API, in modo che tali oggetti possono essere utilizzati come argomenti per i metodi definiti sull'API.

  • Le interfacce "risultato" sono implementate da oggetti all'interno dell'API e resi disponibili ad altre parti del sistema tramite l'API. L'intento di definire un'interfaccia di risultato piuttosto che esporre l'oggetto che lo implementa è di restringere la visione dell'oggetto al mondo esterno.

Per raccogliere un esempio, un sotto-sistema di pagamenti potrebbe definire IPayableItem come interfaccia Role, implementata da molti tipi in altre parti dell'applicazione affinché pagamenti possono essere generati per loro. Gli oggetti di pagamento generati possono essere recuperati tramite l'API, ma definiti dall'interfaccia IP dell'interfaccia Result.

L'unico modo che posso attualmente distinguere è nominare convenzione e/o commento. Idealmente, mi piacerebbe che la distinzione venisse applicata dalla lingua e che applicasse la regola: non è possibile implementare un'interfaccia Result all'esterno dell'API, ma solo usarla. Ma C# non fornisce alcun meccanismo di questo tipo. (Qualcuno può consigliarmi su una lingua che lo fa?). Potrei definire un attributo, ma questo ancora non imporrebbe nulla.

Un altro importante significato della distinzione risiede nella versione semantica dell'API. Se aggiungo un nuovo membro a un'interfaccia Role, questo dovrebbe essere visto come una modifica irrisolta (e quindi una versione di primo livello) - perché qualsiasi implementazione esterna esistente dovrà aggiungere quel membro. Ma se aggiungo un membro a ciò che considero un'interfaccia 'Risultato', allora dovrebbe essere solo il mio codice a essere influenzato - è solo una nuova funzionalità (versione di secondo livello) per tutti gli altri. Ma senza distinzione forzata tra i due tipi, c'è il rischio che le persone stiano implementando le interfacce dei risultati e quindi il loro codice sarebbe rotto.

Qualcun altro ha incontrato questo dilemma? Se è così, come l'hai affrontato? Attendo con ansia le tue risposte.

Ma per favore non sia per rispondere con uno dei seguenti argomenti (che ho sentito troppo spesso):

  • mie interfacce dei risultati dovrebbero essere le classi astratte, piuttosto che le interfacce. Questo non risolve il problema e potenzialmente lo rende peggiore, dal momento che il codice esterno può sottoclassarli.

  • Dovrei restituire il tipo concreto e garantire che tutto ciò che non desidero accessibile all'esterno dell'API sia contrassegnato come "interno". Ci sono molti casi in cui ho bisogno che le cose all'interno dell'API siano pubbliche, ad es. essere accessibile ad altri framework (non attraverso l'API).

+1

'non è possibile implementare un'interfaccia Result al di fuori dell'API, solo usarlo. Quindi presumibilmente l'API restituisce sempre un'interfaccia 'Result' e non ha metodi che accettano un risultato come argomento. In questo caso, cosa importa se un client implementa una classe con un'interfaccia 'Result'? Non possono comunque fare nulla (almeno per quanto riguarda la tua API). Tutto quello che fanno con esso è completamente su di loro. Se stanno cercando di avvolgere la tua API e aggiungere alcune funzionalità extra (implementando il proprio 'Risultato'), perché dovresti fermarli comunque? –

+0

* Ci sono molti casi in cui ho bisogno che le cose all'interno dell'API siano pubbliche, ad es. essere accessibile ad altri framework (non attraverso l'API). * - intendi framework di terze parti? I framework di terze parti creati sulle tue API sono come qualsiasi altro consumatore, il che significa che queste interfacce sono semanticamente pubbliche e le convenzioni, la documentazione e il controllo delle versioni dovrebbero rispecchiare questo aspetto. Se * non * indica API di terze parti, possono essere interne ed esposte ad altri assembly interni tramite 'InternalsVisibleTo'. –

+0

@Matt Anche se la mia interfaccia di risultato non è mai utilizzata come input param sull'API, è ancora importante - e ho fornito una delle ragioni nel mio post originale - vedi i miei commenti su SemVer. Ma è anche perfettamente legittimo che una di queste interfacce di risultato sia utilizzata come parametro di input, e quindi voglio essere sicuro che si tratta di un oggetto creato all'interno dell'API. –

risposta

1

Penso che quello che chiedi è possibile esporre un'interfaccia, ma determinare che una determinata istanza è quella che hai creato?

In tal caso, è anche possibile creare un'interfaccia privata interna e contrassegnare tutte le implementazioni come anche l'implementazione dell'interfaccia privata. Quindi, dopo aver ricevuto un oggetto dal mondo esterno, verificare che abbia anche l'implementazione dell'interfaccia interna.

public interface IPublic 
{ 
    ... 
} 

internal interface ISecret { } 

public class PublicImplementation : IPublic, ISecret 
{ 
    ... 
} 

Solo è possibile implementare l'ISecret, quindi, anche se qualcuno implementa l'IPublic e lo passa a voi, fallirà il test ISecret.

+0

Sì, questo risolve uno dei problemi. Preferirei un meccanismo più intenzionale che impediva o avvisava lo sviluppatore in fase di compilazione. –

+0

Più in generale, ho cercato di eliminare i casi in cui le mie interfacce dei risultati sono utilizzate anche come parametri di input sull'API. Laddove questo non può essere eliminato, cerco di spostare l'intero metodo nell'interfaccia Result, meno un parametro. Nondimeno, resto sorpreso che non ci sia un modo più elegante e intenzionale per definire i due archetipi di interfaccia. –