Come evolvi e versione un'interfaccia?


22

Supponi di avere un'interfaccia IFoo:

public interface IFoo {
    void Bar(string s);
    int Quux(object o);
}

Nella versione 2 dell'API, è necessario aggiungere un metodo Glarga questa interfaccia. Come si fa senza interrompere gli utenti API esistenti e mantenendo la compatibilità con le versioni precedenti? Questo è principalmente rivolto a .NET, ma può applicarsi anche ad altri framework e linguaggi.


Puoi aggiungere senza problemi. Il problema si presenta quando cambi / rimuovi qualcosa che era già lì.
Rig

1
@Rig: almeno in C #, otterrai un errore di compilazione se aggiungi un metodo a un'interfaccia e non lo aggiungi alle classi che implementano tale interfaccia.
Malice,

Bene, è vero. Stavo pensando di più allo scenario di classe utente che potrebbe essere di caos minimo rispetto alla modifica di una firma del metodo o all'eliminazione di una. Quindi suppongo che potrebbe portare a qualche lavoro se fosse necessario aggiungere alla tua interfaccia.
Rig

Risposte:


9

Nella versione 2 dell'API, è necessario aggiungere un metodo Glarga questa interfaccia.

Perché?

Le interfacce definite per l'uso con un'API hanno due ruoli completamente diversi:

  1. Inversione di dipendenza: tali interfacce vengono utilizzate dall'API. Consentono al codice client di creare plugin ecc.
  2. Astrazione: tali interfacce vengono restituite dall'API e nascondono i dettagli di implementazione degli oggetti restituiti.

Ora per una determinata versione di un'API, la stessa interfaccia può agire come entrambe. Tuttavia, nelle versioni future, questo può essere disaccoppiato.

  1. Vuoi estrarre più informazioni dall'interfaccia che consumi. Per migliorare le prestazioni o aggiungere flessibilità o altro. Definisci una nuova interfaccia, possibilmente derivata da quella vecchia, e costruisci un metodo separato che la consumi. La maggior parte dei linguaggi .NET di AFAIK consente il sovraccarico dei metodi, quindi ciò può accadere senza aggiungere molto ingombro.
  2. Vuoi "restituire di più", ovvero l'astrazione di un oggetto "più ricco" dalla tua API. Qui hai due scelte:

    • Si può ragionevolmente presumere che il codice client non avrà i propri implementatori dell'interfaccia. In base a questo presupposto è sicuro aggiungere le estensioni all'interfaccia esistente.
    • Definire una nuova interfaccia, se possibile derivata dalla precedente. Se tale derivazione è impossibile, creare metodi separati per eseguire una query per istanze della nuova interfaccia o utilizzare la composizione:

      interface MyNewInterface extends MyOldInterface { 
           FancyNewInterface getFancyShit();
      }
      

15

DirectX ha aggiunto i numeri di versione alle sue interfacce. Nel tuo caso, la soluzione sarebbe qualcosa di simile

public interface IFoo2 : IFoo
{
    void Glarg();
}

L'API farebbe comunque riferimento a IFoo e a IFoo2 solo nei metodi ecc. Dove è richiesta la funzionalità IFoo2.

L'implementazione dell'API dovrebbe verificare nei metodi esistenti (= versione 1) se un oggetto parametro IFoo implementa effettivamente IFoo2, se la semantica del metodo è diversa per IFoo2.


3

L'aggiunta di un nuovo metodo (o metodi) all'API deve essere eseguita in modo tale da non avere effetti collaterali sull'API esistente. Soprattutto, qualcuno che continua a utilizzare la vecchia API come se la nuova API non esistesse, non dovrebbe esserne influenzato. L'uso della vecchia API non dovrebbe avere effetti collaterali imprevisti sulla nuova API.

Se uno dei metodi esistenti nell'API è sostituito da quelli nuovi, non rimuoverli immediatamente. Segnali come obsoleti e fornisci una spiegazione su cosa dovrebbe essere usato invece. Ciò avvisa gli utenti del tuo codice che le versioni future potrebbero non supportarlo più invece di rompere il loro codice senza preavviso.

Se le nuove e vecchie API sono incompatibili e non possono vivere insieme senza effetti collaterali indesiderati, separarle e documentare che se la nuova API deve essere adottata, la vecchia API deve essere completamente ritirata. Questo è meno desiderabile in quanto ci sarà sempre qualcuno che tenta di utilizzare entrambi e si sente frustrato quando non funziona.

Dato che hai chiesto informazioni specifiche su .NET, potresti voler leggere questo articolo sulla deprecazione in .NET, che si collega a ObsoleteAttribute(utilizzato nel seguente esempio):

using System;

public sealed class App {
   static void Main() {      
      // The line below causes the compiler to issue a warning:
      // 'App.SomeDeprecatedMethod()' is obsolete: 'Do not call this method.'
      SomeDeprecatedMethod();
   }

   // The method below is marked with the ObsoleteAttribute. 
   // Any code that attempts to call this method will get a warning.
   [Obsolete("Do not call this method.")]
   private static void SomeDeprecatedMethod() { }
}

2

Le modifiche all'interfaccia pubblica comportano la rottura. La strategia comune è di fare queste solo sulle versioni principali e dopo un periodo di blocco (quindi non accade per capriccio). È possibile cavarsela senza interrompere i client se si stanno aggiungendo aggiunte in una nuova interfaccia (e l'implementazione può fornire entrambi nella stessa classe). Non è l'ideale, e se continui a farlo avrai un casino.

Con altri tipi di modifiche (rimozione dei metodi, modifica delle firme), sei bloccato.


2
È possibile riservare preventivamente un prefisso per i nomi dei metodi futuri e avvisare tutti gli utenti che non devono utilizzare quello spazio dei nomi, ma anche ciò rende un'API non elegante. In generale, genitore ha assolutamente ragione: la rimozione (e spesso oltre) dei metodi si romperà gli utenti esistenti, e non c'è niente che tu possa fare, tranne che piano con saggezza.
Kilian Foth

1

Un'interfaccia è un contratto, quindi non dovrebbe avere il controllo delle versioni. Cosa succede se un calciatore ottiene un nuovo contratto? Quello vecchio è ancora valido? No. Se si cambia l'interfaccia, il contratto cambia e il contratto precedente (interfaccia) non è più valido.

Anche se potresti usare la strategia IFoo2, alla fine questo diventerà disordinato quando avrai:

  • IFoo2
  • IFoo3
  • IFoo4
  • eccetera.

Che schifo.

Un'API è diversa. Fornisco una libreria di codice da utilizzare. Il prossimo mese ti darò una biblioteca aggiornata. Come ha detto un altro poster, non interrompere ciò che sto già utilizzando, basta aggiungere nuove funzionalità / metodi.

Se si desidera eseguire la versione di qualcosa, utilizzare una classe abtract anziché un'interfaccia.

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.