Como você evolui e versão uma interface?


22

Digamos que você tenha uma interface IFoo:

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

Na versão 2 da sua API, você precisa adicionar um método Glarga essa interface. Como você faz isso sem quebrar os usuários existentes da API e manter a compatibilidade com versões anteriores? O objetivo é principalmente o .NET, mas também pode ser aplicado a outras estruturas e linguagens.


Você pode adicionar sem problemas. Os problemas surgem quando você altera / remove algo que já estava lá.
Rig

1
@ Rig: Pelo menos em C #, você receberá um erro de compilação se adicionar um método a uma interface e não o adicionar a classes que implementam essa interface.
Malice

Bem, isso é verdade. Eu estava pensando mais no cenário de classe de usuário, que pode ser de um caos mínimo em comparação com a alteração de uma assinatura de método ou a exclusão de uma. Então, suponho que isso poderia levar a algum trabalho, se você precisasse adicionar à sua interface.
Rig

Respostas:


9

Na versão 2 da sua API, você precisa adicionar um método Glarga essa interface.

Por quê?

As interfaces definidas para uso com uma API têm duas funções totalmente diferentes:

  1. Inversão de dependência - essas interfaces são consumidas pela sua API. Eles permitem que o código do cliente crie plugins etc.
  2. Abstração - essas interfaces são retornadas pela sua API e ocultam os detalhes de implementação dos objetos retornados.

Agora, para uma determinada versão de uma API, a mesma interface pode atuar como ambas. Ainda assim, em versões futuras, isso pode ser dissociado.

  1. Você deseja extrair mais informações da interface que consome. Para melhorar o desempenho ou adicionar flexibilidade ou o que for. Defina uma nova interface, possivelmente derivada da antiga, e construa um método separado que a consuma. A maioria das linguagens .NET do AFAIK permite a sobrecarga de métodos, portanto, isso pode acontecer sem adicionar muita confusão.
  2. Você deseja "retornar mais", ou seja, a abstração de um objeto "mais rico" da sua API. Aqui você está tendo duas opções:

    • Você pode razoavelmente supor que o código do cliente não terá seus próprios implementadores da interface. Sob essa suposição, é seguro adicionar suas extensões à interface existente.
    • Defina uma nova interface, se possível derivada da anterior. Se essa derivação for impossível, crie métodos separados para consultar instâncias da nova interface ou use a composição:

      interface MyNewInterface extends MyOldInterface { 
           FancyNewInterface getFancyShit();
      }
      

15

O DirectX adicionou números de versão às suas interfaces. No seu caso, a solução seria algo como

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

A API ainda se referiria ao IFoo e ao IFoo2 apenas em métodos etc, onde a funcionalidade do IFoo2 é necessária.

A implementação da API deve verificar nos métodos existentes (= versão 1) se um objeto de parâmetro IFoo realmente implementa o IFoo2, se a semântica do método for diferente para o IFoo2.


3

A adição de um novo método (ou métodos) à sua API deve ser feita de forma a não causar efeitos colaterais na API existente. Mais importante, alguém que continua a usar a API antiga como se a nova API não existisse, não deve ser afetado por ela. O uso da API antiga também não deve ter efeitos colaterais inesperados na nova API.

Se algum dos métodos existentes na API for substituído pelos novos, não os remova imediatamente. Marque-os como obsoletos e forneça uma explicação sobre o que deve ser usado. Isso avisa os usuários do seu código que versões futuras podem não mais dar suporte a ele, em vez de quebrar o código sem aviso.

Se as APIs novas e antigas forem incompatíveis e não puderem viver juntas sem efeitos colaterais indesejados, separe-as e documente que, se a nova API for adotada, a API antiga deverá ser completamente retirada. Isso é menos desejável, pois sempre haverá alguém que tenta usar os dois e fica frustrado quando não funciona.

Como você perguntou sobre o .NET especificamente, convém ler este artigo sobre descontinuação no .NET, que possui links para o ObsoleteAttribute(usado no exemplo a seguir):

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

Alterações na interface pública envolvem quebra. A estratégia comum é fazer isso apenas nas versões principais e após um período de congelamento (para que isso não ocorra por um capricho). Você pode fugir sem prejudicar seus clientes se estiver adicionando adições a uma nova interface (e sua implementação pode fornecer ambas na mesma classe). Isso não é o ideal, e se você continuar fazendo isso, terá uma bagunça.

Porém, com outros tipos de modificação (remoção de métodos, alteração de assinaturas), você fica preso.


2
Você pode reservar preventivamente um prefixo para nomes de métodos futuros e avisar a todos os usuários que eles não devem usar esse espaço para nome, mas mesmo isso cria uma API deselegante. Em geral, os pais é absolutamente certo: a remoção (e muitas vezes adição) de métodos vai quebrar os usuários existentes, e não há nada que você possa fazer sobre isso exceto plano-lo com sabedoria.
precisa saber é o seguinte

1

Uma interface é um contrato, portanto, não deve ter controle de versão. O que acontece se um jogador de futebol conseguir um novo contrato? O antigo ainda é válido? Não. Se alguém alterar a interface, o contrato será alterado e o contrato anterior (interface) não será mais válido.

Embora você possa usar a estratégia IFoo2, eventualmente isso ficará confuso quando você tiver:

  • IFoo2
  • IFoo3
  • IFoo4
  • etc.

Que nojo.

Uma API é diferente. Dou biblioteca de código para usar. No próximo mês, eu lhe darei uma biblioteca atualizada. Como outro pôster disse, não quebre o que já estou usando, basta adicionar novas funcionalidades / métodos.

Se você quiser versão alguma coisa, use uma classe abtract em vez de uma interface.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.