Evoluindo uma interface que não deveria ser implementada pelo cliente

5

Estou prestes a escrever uma biblioteca Java. Basicamente, essa biblioteca fornece algo assim para o usuário:

interface Foo {
  void doA();
  boolean aWorked();
  void doB(int value);
}

O usuário não deve implementar essa interface (obviamente). Assim, o código do usuário será parecido com isto:

Foo f = Library::SomeFactory();
if (someDecison()) {
  f.doA();
  if (!f.aWorked()) {
    f.doB(21);
  }
} else {
  f.doB(42);
}

Posso garantir que nem as pré-condições nem as pós-condições dos métodos na interface sejam alteradas no futuro. Mas pode haver um novo método, digamos doC() .

Agora, vários recursos, incluindo Documentos do Orcale , sugerem que basta adicionar esse doC() a interface acima é uma má ideia. Eles propõem diferentes soluções de estender a interface ...

interface Foo2 : extends Foo {
  void doC();
}

... para soprar o código com padrões de comando e quais não. Mas a razão de suporte é sempre que "todas as classes implementadas devem ser alteradas". Isso não é um problema no meu caso, já que todas as classes implementadas estão "sob meu controle" e precisará ser alterado de qualquer forma (quando houver um doC() ).

É simplesmente adicionar o método a Foo realmente uma má ideia? E se sim, por que? Existe alguma coisa que eu não estou levando em conta aqui? Meu principal objetivo é não quebrar nenhum código de usuário escrito nessa interface.

// That's what I'm planning
interface Foo {
  void doA();
  boolean aWorked();
  void doB(int value);
  void doC();
}

Esta fonte confirma minha sensação de que pode ser tão simples assim:

[..] If the method is added to a class (interface) which Clients are not allowed to subclass (to implement), then it is not a breaking change. [..]

    
por Daniel Jour 27.10.2016 / 01:17
fonte

4 respostas

4

Não há nenhum mecanismo Java para impedir que os clientes implementem uma interface acessível a eles. O texto que você está citando diz "não é permitido implementar". Infelizmente, a única maneira que pode ser verdadeira (reforçada) é se a interface estiver inacessível ao cliente.

Existem razões pelas quais um cliente pode querer implementar essa interface; por exemplo, se eles estiverem tentando reunir várias bibliotecas diferentes e acharem que sua interface é uma boa abstração para reutilização.

Você pode alertar seus clientes sobre a implementação de sua interface - que sua intenção é estendê-la ou, você pode usar os novos métodos padrão do Java quando estender a interface (ou ambos).

    
por 27.10.2016 / 01:37
fonte
3

As melhores práticas são boas e elegantes, mas é importante ser pragmático.

Neste caso, não vejo nada de errado em ampliar sua interface no local no futuro. Como você mencionou, as únicas classes afetadas estão sob seu controle.

Além disso, por que ter um membro aWorked() se você pode simplesmente ter doA() retornando um resultado de sucesso / falha?

    
por 27.10.2016 / 01:27
fonte
1

Evoluindo uma interface sem quebrar o código

Quaisquer novos métodos devem fornecer o comportamento padrão , que é mais fácil (e mais claro alternativa à definição de um novo subtipo.

Por que adicionar um novo método abstrato a uma interface é ruim?

Ele faz exatamente o que você quer evitar: quebrar o código. Se os clientes usaram uma versão mais recente de sua estrutura para correções de bugs, elas serão forçadas a declarar o novo método em qualquer tipo de implementação da interface.

Impedindo que os clientes implementem sua interface

Mesmo se o Java suportasse isso, o que não é, eu aconselharia contra isso. Você limitaria o uso do seu framework.

Se sua estrutura não tiver uma implementação que o cliente possa exigir / queira, eles (ou qualquer outra pessoa, talvez um desenvolvedor mais experiente) devem poder estender o Foo para expor novas implementações úteis que você pode não ter tido tempo escrever, ou talvez nunca tenha pensado.

    
por 02.11.2016 / 03:16
fonte
0

Se a interface não deve ser implementada pelo cliente, você deve torná-la privada para o pacote para que os clientes nem saibam que ela existe. Se definir um tipo que faz parte de sua API pública, você poderá torná-lo uma superclasse de suas próprias implementações. A superclasse pode ter abstract ou ter um construtor privado de pacote, e as implementações podem ser fechadas para extensão declarando-as final ou restringindo o acesso a seus construtores.

Você poderá remover essas restrições posteriormente se descobrir um motivo convincente para isso. Mas você não pode adicioná-los depois sem arriscar a quebra no código de terceiros. Por isso, é uma boa prática em uma API pública limitar a visibilidade e a extensibilidade de tudo o máximo possível e abri-lo mais tarde.

    
por 04.11.2016 / 03:38
fonte