Preciso usar uma interface quando apenas uma classe irá implementá-la?

209

O ponto inteiro de uma interface não é para várias classes aderirem a um conjunto de regras e implementações?

    
por Lamin Sanneh 07.08.2012 / 09:38
fonte

15 respostas

183

Estritamente falando, não, você não, YAGNI se aplica. Dito isso, o tempo que você gasta criando a interface é mínimo, especialmente se você tiver uma ferramenta útil de geração de código que faz a maior parte do trabalho para você. Se você não tem certeza se vai precisar da interface ou não, eu diria que é melhor errar do lado de apoiar a definição de uma interface.

Além disso, o uso de uma interface, mesmo para uma única classe, fornecerá outra implementação simulada para testes de unidade, um que não está em produção. A resposta de Avner Shahar-Kashtan se expande nesse ponto.

    
por 07.08.2012 / 09:45
fonte
123

Eu responderia que se você precisa de uma interface ou não não depende de quantas classes irão implementá-la. Interfaces são uma ferramenta para definir contratos entre vários subsistemas da sua aplicação; Então, o que realmente importa é como seu aplicativo é dividido em subsistemas. Deve haver interfaces como o front-end para os subsistemas encapsulados, não importa quantas classes os implementem.

Aqui está uma regra muito útil:

  • Se você fizer a classe Foo referir-se diretamente à classe BarImpl , estará se comprometendo a alterar Foo sempre que alterar BarImpl . Você está basicamente tratando-os como uma unidade de código dividida em duas classes.
  • Se você fizer Foo se referir à interface Bar , estará se comprometendo a evitar as alterações em Foo quando alterar BarImpl .

Se você definir interfaces nos pontos-chave do seu aplicativo, você deve pensar cuidadosamente nos métodos que eles devem suportar e quais não devem, e você comenta as interfaces claramente para descrever como uma implementação deve se comportar (e como não), seu aplicativo será muito mais fácil de entender, porque essas interfaces comentadas fornecerão uma espécie de especificação do aplicativo - uma descrição de como é destinado a se comportar. Isso torna muito mais fácil ler o código (em vez de perguntar "o que diabos esse código deve fazer", você pode perguntar "como esse código faz o que deve fazer").

Além de tudo isso (ou, na verdade, por causa disso), as interfaces promovem a compilação separada. Como as interfaces são triviais para compilar e têm menos dependências do que suas implementações, isso significa que se você escrever classe Foo para usar uma interface Bar , normalmente é possível recompilar BarImpl sem precisar recompilar Foo . Em grandes aplicações, isso pode economizar muito tempo.

    
por 07.08.2012 / 20:00
fonte
93

As interfaces são designadas para definir um comportamento, ou seja, um conjunto de protótipos de funções / métodos. Os tipos que implementam a interface implementarão esse comportamento, portanto, quando você lida com esse tipo, você sabe (parcialmente) qual comportamento ele tem.

Não há necessidade de definir uma interface se você souber que o comportamento definido por ela será usado apenas uma vez. KISS (mantenha-o simples, estúpido)

    
por 07.08.2012 / 09:47
fonte
62

Embora, em teoria, você não deva ter uma interface apenas para ter uma interface, a resposta de Yannis Rizos sugere outras complicações:

Quando você está escrevendo testes de unidade e usando estruturas simuladas, como Moq ou FakeItEasy (para nomear as duas mais recentes que usei), você está implicitamente criando outra classe que implementa a interface. A pesquisa no código ou na análise estática pode afirmar que há apenas uma implementação, mas na verdade há a implementação simulada interna. Sempre que você começar a escrever mocks, descobrirá que extrair interfaces faz sentido.

Mas espere, tem mais. Existem mais cenários onde existem implementações de interface implícitas. A utilização da pilha de comunicação WCF do .NET, por exemplo, gera um proxy para um serviço remoto, que, novamente, implementa a interface.

Em um ambiente de código limpo, concordo com o restante das respostas aqui. No entanto, preste atenção a quaisquer estruturas, padrões ou dependências que você possa usar interfaces.

    
por 07.08.2012 / 10:02
fonte
19

Parece que as respostas dos dois lados da cerca podem ser resumidas aqui:

Design well, and put interfaces where interfaces are needed.

Como observei na minha resposta à resposta de Yanni , não acho que você possa ter dificuldades e regra rápida sobre interfaces. A regra precisa ser, por definição, flexível. Minha regra nas interfaces é que uma interface deve ser usada em qualquer lugar em que você esteja criando uma API. E uma API deve ser criada em qualquer lugar que você esteja cruzando a fronteira de um domínio de responsabilidade para outro.

Para um exemplo (horrivelmente inventado), digamos que você esteja criando uma Car de classe. Na sua turma, você certamente precisará de uma camada de interface do usuário. Nesse exemplo específico, ele assume a forma de IginitionSwitch , SteeringWheel , GearShift , GasPedal e BrakePedal . Como esse carro contém um AutomaticTransmission , você não precisa de ClutchPedal . (E como este é um carro terrível, não há A / C, rádio ou assento. Na verdade, as tábuas do assoalho estão faltando também - você só precisa segurar o volante e esperar o melhor!)

Então, qual dessas classes precisa de uma interface? A resposta poderia ser todas elas, ou nenhuma delas - dependendo do seu design.

Você poderia ter uma interface assim:

Interface ICabin
    Event IgnitionSwitchTurnedOn()
    Event IgnitionSwitchTurnedOff()
    Event BrakePedalPositionChanged(int percent)
    Event GasPedalPositionChanged(int percent)
    Event GearShiftGearChanged(int gearNum)
    Event SteeringWheelTurned(float degree)
End Interface

Nesse ponto, o comportamento dessas classes torna-se parte da Interface ICabin / API. Neste exemplo, as classes (se houver alguma) são provavelmente simples, com algumas propriedades e uma ou duas funções. E o que você está implicitamente afirmando com seu design é que essas classes existem apenas para suportar qualquer implementação concreta do ICabin que você tenha, e elas não podem existir sozinhas, ou elas são sem sentido fora do contexto da ICabin.

É a mesma razão pela qual você não testa membros privados - eles existem somente para suportar a API pública e, portanto, seu comportamento deve ser testado testando a API.

Portanto, se a sua classe existe somente para oferecer suporte a outra classe e, conceitualmente, você a vê como não tendo realmente um domínio próprio, não há problema em pular a interface. Mas se a sua turma é importante o suficiente para você considerar que ela cresceu o suficiente para ter seu próprio domínio, vá em frente e forneça uma interface para ela.

EDITAR:

Freqüentemente (incluindo nesta resposta) você lerá coisas como 'domínio', 'dependência' (frequentemente associadas a 'injeção') que não significam nada quando você está começando a programar não significou nada para mim). Para domínio, significa exatamente o que parece:

The territory over which dominion or authority is exerted; the possessions of a sovereign or commonwealth, or the like. Also used figuratively. [WordNet sense 2] [1913 Webster]

Nos termos do meu exemplo, vamos considerar o IgnitionSwitch . Em um carro do espaço, o interruptor de ignição é responsável por:

  1. Autenticando (não identificando) o usuário (eles precisam da chave correta)
  2. Fornecendo corrente ao acionador de partida para que ele possa realmente ligar o carro
  3. Fornecendo corrente ao sistema de ignição para que ele possa continuar a operar
  4. Desligar a corrente para que o carro pare.
  5. Dependendo de como você o visualiza, na maioria (todos?) carros mais novos há um interruptor que impede que a chave seja removida da ignição enquanto a transmissão está fora do Parque, portanto, isso pode fazer parte de seu domínio. (Na verdade, isso significa que eu preciso repensar e redesenhar meu sistema ...)

Essas propriedades formam o domínio do IgnitionSwitch ou, em outras palavras, o que ele conhece e é responsável.

O IgnitionSwitch não é responsável pelo GasPedal . O interruptor de ignição é completamente ignorante do pedal do acelerador em todos os sentidos. Ambos operam completamente independentes um do outro (apesar de um carro ser bastante inútil sem os dois!).

Como afirmei originalmente, isso depende do seu design. Você pode criar um IgnitionSwitch que tenha dois valores: Ativado ( True ) e Desativado ( False ). Ou você pode projetá-lo para autenticar a chave fornecida e uma série de outras ações. Essa é a parte difícil de ser um desenvolvedor que está decidindo onde desenhar as linhas na areia - e honestamente a maior parte do tempo é completamente relativo. No entanto, essas linhas na areia são importantes - é aí que está sua API e, portanto, onde devem estar suas interfaces.

    
por 08.08.2012 / 13:41
fonte
19

Não, você não precisa deles, e considero um antipadrão para criar interfaces automaticamente para cada referência de classe.

Existe um custo real para fazer Foo / FooImpl para tudo. O IDE pode criar a interface / implementação gratuitamente, mas quando você está navegando no código, você tem a carga cognitiva extra de F3 / F12 em foo.doSomething() levando você para a assinatura da interface e não a implementação real desejada. Além disso, você tem a desordem de dois arquivos em vez de um para tudo.

Então você só deve fazer isso quando realmente precisar de algo.

Agora, abordando os contra-argumentos:

Eu preciso de interfaces para estruturas de injeção de dependência

Interfaces para suportar frameworks são legadas. Em Java, as interfaces costumavam ser um requisito para proxies dinâmicos, pré-CGLIB. Hoje, você geralmente não precisa disso. É considerado um progresso e um benefício para a produtividade do desenvolvedor que você não precisa mais deles em EJB3, Spring etc.

Preciso de zombarias para testes unitários

Se você escreve seus próprios mocks e tem duas implementações reais, então uma interface é apropriada. Nós provavelmente não estaríamos tendo essa discussão em primeiro lugar se sua base de código tivesse tanto um FooImpl quanto um TestFoo.

Mas se você estiver usando uma estrutura de simulação como Moq, EasyMock ou Mockito, você pode simular classes e não precisar de interfaces. É análogo à configuração de foo.method = mockImplementation em uma linguagem dinâmica na qual os métodos podem ser atribuídos.

Precisamos de interfaces para seguir o Princípio de Inversão de Dependência (DIP)

O DIP diz que você constrói para depender de contratos (interfaces) e não de implementações. Mas uma classe é um contrato e uma abstração. É para isso que servem as palavras-chave públicas / privadas. Na universidade, o exemplo canônico era algo como uma classe Matrix ou Polinomial - os consumidores têm uma API pública para criar matrizes, adicioná-los etc., mas não podem se importar se a matriz é implementada em forma esparsa ou densa. Não foi necessário IMatrix ou MatrixImpl para provar esse ponto.

Além disso, o DIP muitas vezes é aplicado em excesso a cada nível de chamada de classe / método, não apenas nos principais limites do módulo. Um sinal de que você está aplicando demais o DIP é que a interface e a implementação mudam na etapa de bloqueio, de modo que você precise tocar em dois arquivos para fazer uma alteração em vez de um. Se o DIP for aplicado adequadamente, isso significa que sua interface não precisará ser alterada com frequência. Além disso, outro sinal é que sua interface tem apenas um consumidor real (seu próprio aplicativo). História diferente se você estiver criando uma biblioteca de classes para consumo em muitos aplicativos diferentes.

Este é um corolário do ponto de zombaria do tio Bob Martin - você só precisa zombar dos principais limites arquitetônicos. Em um webapp, o acesso HTTP e DB são os principais limites. Todas as chamadas de classe / método entre não são. O mesmo vale para o DIP.

Veja também:

por 26.07.2015 / 14:02
fonte
8

Não (YAGNI) , a menos que você esteja planejando escrever testes para outras classes usando essa interface, e testes se beneficiariam de zombar da interface.

    
por 07.08.2012 / 09:45
fonte
8

De MSDN :

Interfaces are better suited to situations in which your applications require many possibly unrelated object types to provide certain functionality.

Interfaces are more flexible than base classes because you can define a single implementation that can implement multiple interfaces.

Interfaces are better in situations in which you do not need to inherit implementation from a base class.

Interfaces are useful in cases where you cannot use class inheritance. For example, structures cannot inherit from classes, but they can implement interfaces.

Geralmente, no caso de uma única classe, não será necessário implementar uma interface, mas considerando o futuro do seu projeto, pode ser útil definir formalmente o comportamento necessário das classes.

    
por 07.08.2012 / 09:56
fonte
5

Para responder à pergunta: há mais do que isso.

Um aspecto importante de uma interface é a intenção.

Uma interface é "um tipo abstrato que não contém dados, mas expõe comportamentos" - Interface (computação) Então, se este é um comportamento, ou um conjunto de comportamentos, que a classe suporta, então uma interface é provavelmente o padrão correto. Se, no entanto, o (s) comportamento (s) for (são) intrínseco (s) ao conceito incorporado pela classe, então você provavelmente não desejará uma interface.

A primeira pergunta a fazer é qual é a natureza da coisa ou processo que você está tentando representar. Em seguida, siga as razões práticas para implementar essa natureza de uma determinada maneira.

    
por 07.08.2012 / 14:55
fonte
5

Como você fez essa pergunta, suponho que você já tenha visto o interesse de ter uma interface ocultando várias implementações. Isso pode ser manifestado pelo princípio da inversão de dependência.

No entanto, a necessidade de ter uma interface ou não não depende do número de suas implementações. O papel real de uma interface é que ela define um contrato informando qual serviço deve ser fornecido em vez de como ele deve ser implementado.

Uma vez definido o contrato, duas ou mais equipes podem trabalhar de forma independente. Digamos que você esteja trabalhando em um módulo A e dependa do módulo B, o fato de criar uma interface em B permite continuar seu trabalho sem se preocupar com a implementação de B porque todos os detalhes estão ocultos pela interface. Assim, a programação distribuída torna-se possível.

Mesmo que o módulo B tenha apenas uma implementação de sua interface, a interface ainda é necessária.

Em conclusão, uma interface oculta detalhes de implementação de seus usuários. A programação para interface ajuda a escrever mais documentos porque o contrato deve ser definido, a escrever software mais modular, a promover testes de unidade e a acelerar a velocidade de desenvolvimento.

    
por 13.12.2012 / 11:56
fonte
4

Todas as respostas aqui são muito boas. Na verdade, na maioria das vezes você não precisa implementar uma interface diferente. Mas há casos em que você pode querer fazer isso de qualquer maneira. Aqui estão alguns casos em que eu faço:

A classe implementa outra interface que não quero expor
Acontece frequentemente com a classe adaptadora que faz a ponte com o código de terceiros.

interface NameChangeListener { // Implemented by a lot of people
    void nameChanged(String name); 
} 

interface NameChangeCount { // Only implemented by my class
    int getCount();
}

class NameChangeCounter implements NameChangeListener, NameChangeCount {
    ...
}

class SomeUserInterface {
    private NameChangeCount currentCount; // Will never know that you can change the counter
}

A classe usa uma tecnologia específica que não deve vazar
Principalmente ao interagir com bibliotecas externas. Mesmo se houver apenas uma implementação, eu uso uma interface para garantir que não introduza acoplamentos desnecessários com a biblioteca externa.

interface SomeRepository { // Guarantee that the external library details won't leak trough
    ...
}

class OracleSomeRepository implements SomeRepository { 
    ... // Oracle prefix allow us to quickly know what is going on in this class
}

Comunicação entre camadas
Mesmo que apenas uma classe de interface de usuário implemente uma das classes de domínio, ela permite uma melhor separação entre essas camadas e, mais importante, evita a dependência cíclica.

package project.domain;

interface UserRequestSource {
    public UserRequest getLastRequest();
}

class UserBehaviorAnalyser {
    private UserRequestSource requestSource;
}

package project.ui;

class OrderCompleteDialog extends SomeUIClass implements project.domain.UserRequestSource {
    // UI concern, no need for my domain object to know about this method.
    public void displayLabelInErrorMode(); 

    // They most certainly need to know about *that* though
    public UserRequest getLastRequest();
}

Apenas um subconjunto do método deve estar disponível para a maioria dos objetos
Principalmente acontece quando eu tenho algum método de configuração na classe concreta

interface Sender {
    void sendMessage(Message message)
}

class PacketSender implements Sender {
    void sendMessage(Message message);
    void setPacketSize(int sizeInByte);
}

class Throttler { // This class need to have full access to the object
    private PacketSender sender;

    public useLowNetworkUsageMode() {
        sender.setPacketSize(LOW_PACKET_SIZE);
        sender.sendMessage(new NotifyLowNetworkUsageMessage());

        ... // Other details
    }
}

class MailOrder { // Not this one though
    private Sender sender;
}

Então, no final, eu uso a interface pela mesma razão que eu uso o campo privado: outro objeto não deveria ter acesso a coisas que eles não deveriam acessar. Se eu tenho um caso assim, eu introduzo uma interface mesmo que apenas uma classe implemente.

    
por 13.12.2012 / 20:55
fonte
2

As interfaces são muito importantes, mas tente controlar o número delas que você tem.

Tendo percorrido o caminho da criação de interfaces para praticamente tudo, é fácil acabar com o código de "spaghetti picado". Eu adiro a maior sabedoria de Ayende Rahien, que postou algumas palavras muito sábias sobre o assunto:

link

Este é seu primeiro post de uma série inteira, então continue lendo!

    
por 07.08.2012 / 15:30
fonte
2

Uma das razões pelas quais você ainda pode querer introduzir uma interface nesse caso é seguir o Princípio de Inversão de Dependência . Ou seja, o módulo que usa a classe dependerá de uma abstração dele (ou seja, a interface) em vez de depender de uma implementação concreta. Ele separa componentes de alto nível dos componentes de baixo nível.

    
por 08.08.2012 / 03:50
fonte
2

Não há motivo real para fazer nada. Interfaces são para ajudar você e não o programa de saída. Portanto, mesmo que a interface seja implementada por um milhão de classes, não há uma regra que diga que você precisa criar uma. Você cria um para que quando você, ou qualquer outra pessoa que use seu código, queira alterar algo, ele se infiltra em todas as implementações. Criar uma interface ajudará você em todos os casos futuros em que você pode querer criar outra classe que a implemente.

    
por 13.12.2012 / 18:43
fonte
1

Nem sempre há necessidade de definir uma interface para uma classe.

Objetos simples como objetos de valor não possuem várias implementações. Eles não precisam ser ridicularizados. A implementação pode ser testada por conta própria e quando outras classes são testadas que dependem delas, o objeto de valor real pode ser usado.

Lembre-se de que criar uma interface tem um custo. Ele precisa ser atualizado ao longo da implementação, ele precisa de um arquivo extra e algum IDE terá problemas para ampliar a implementação, não a interface.

Então, eu definiria interfaces apenas para classes de nível mais alto, onde você deseja uma abstração da implementação.

Observe que, com uma turma, você obtém uma interface gratuitamente. Além da implementação, uma classe define uma interface do conjunto de métodos públicos. Essa interface é implementada por todas as classes derivadas. Não é estritamente falando uma interface, mas pode ser usado exatamente da mesma maneira. Então, não acho necessário recriar uma interface que já existe sob o nome da classe.

    
por 25.08.2014 / 12:12
fonte