O que significa “programar para uma interface”?

Dec 21 2008

Já vi isso ser mencionado algumas vezes e não tenho certeza do que significa. Quando e por que você faria isso?

Eu sei o que as interfaces fazem, mas o fato de não ter claro isso me faz pensar que estou perdendo usá-las corretamente.

Seria assim se você fizesse:

IInterface classRef = new ObjectWhatever()

Você poderia usar qualquer classe que implemente IInterface? Quando você precisa fazer isso? A única coisa que posso pensar é se você tem um método e não tem certeza de qual objeto será passado, exceto para sua implementação IInterface. Não consigo imaginar quantas vezes você precisaria fazer isso.

Além disso, como você poderia escrever um método que aceita um objeto que implementa uma interface? Isso é possível?

Respostas

1680 PeterMeyer Dec 21 2008 at 10:43

Existem algumas respostas maravilhosas aqui para essas perguntas que entram em todos os tipos de grandes detalhes sobre interfaces e código de acoplamento frouxo, inversão de controle e assim por diante. Existem algumas discussões bastante intensas, então eu gostaria de aproveitar a oportunidade para analisar um pouco as coisas para entender por que uma interface é útil.

Quando comecei a me expor às interfaces, também fiquei confuso sobre sua relevância. Eu não entendi porque você precisava deles. Se estivermos usando uma linguagem como Java ou C #, já temos herança e vejo as interfaces como uma forma mais fraca de herança e pensei, "por que se preocupar?" Em certo sentido, eu estava certo, você pode pensar em interfaces como uma espécie de forma fraca de herança, mas, além disso, eu finalmente entendi seu uso como uma construção de linguagem pensando nelas como um meio de classificar características ou comportamentos comuns que foram exibidos por potencialmente muitas classes de objetos não relacionados.

Por exemplo - digamos que você tenha um jogo SIM e tenha as seguintes classes:

class HouseFly inherits Insect {
    void FlyAroundYourHead(){}
    void LandOnThings(){}
}

class Telemarketer inherits Person {
    void CallDuringDinner(){}
    void ContinueTalkingWhenYouSayNo(){}
}

Claramente, esses dois objetos não têm nada em comum em termos de herança direta. Mas, você poderia dizer que ambos são irritantes.

Digamos que nosso jogo precise ter algum tipo de coisa aleatória que irrite o jogador quando ele janta. Pode ser a HouseFlyou a Telemarketerou ambos - mas como você permite ambos com uma única função? E como você pede a cada tipo diferente de objeto para "fazer suas coisas irritantes" da mesma maneira?

A chave para perceber é que tanto a Telemarketerquanto HouseFlycompartilham um comportamento comum interpretado de maneira vaga, embora não sejam nada parecidos em termos de modelagem. Então, vamos fazer uma interface que ambos possam implementar:

interface IPest {
    void BeAnnoying();
}

class HouseFly inherits Insect implements IPest {
    void FlyAroundYourHead(){}
    void LandOnThings(){}

    void BeAnnoying() {
        FlyAroundYourHead();
        LandOnThings();
    }
}

class Telemarketer inherits Person implements IPest {
    void CallDuringDinner(){}
    void ContinueTalkingWhenYouSayNo(){}

    void BeAnnoying() {
        CallDuringDinner();
        ContinueTalkingWhenYouSayNo();
    }
}

Agora temos duas classes que podem ser irritantes cada uma à sua maneira. E eles não precisam derivar da mesma classe base e compartilhar características inerentes comuns - eles simplesmente precisam satisfazer o contrato de IPest- esse contrato é simples. Você apenas tem que fazer BeAnnoying. Nesse sentido, podemos modelar o seguinte:

class DiningRoom {

    DiningRoom(Person[] diningPeople, IPest[] pests) { ... }

    void ServeDinner() {
        when diningPeople are eating,

        foreach pest in pests
        pest.BeAnnoying();
    }
}

Aqui temos uma sala de jantar que aceita vários comensais e várias pragas - observe o uso da interface. Isso significa que em nosso pequeno mundo, um membro da pestsmatriz pode ser na verdade um Telemarketerobjeto ou um HouseFlyobjeto.

O ServeDinnermétodo é chamado quando o jantar é servido e nosso pessoal na sala de jantar deve comer. Em nosso joguinho, é quando nossas pragas fazem seu trabalho - cada praga é instruída a ser irritante por meio da IPestinterface. Desta forma, podemos facilmente ter ambos Telemarketerse HouseFlysser irritantes em cada um de seus próprios caminhos - nos preocupamos apenas que temos algo no DiningRoomobjeto que é uma praga, realmente não nos importamos com o que é e eles não poderiam ter nada em comum com outro.

Este exemplo de pseudocódigo muito elaborado (que se arrastou por muito mais tempo do que eu esperava) serve simplesmente para ilustrar o tipo de coisa que finalmente acendeu a luz para mim em termos de quando poderíamos usar uma interface. Peço desculpas antecipadamente pela tolice do exemplo, mas espero que ajude no seu entendimento. E, com certeza, as outras respostas postadas que você recebeu aqui realmente cobrem a gama do uso de interfaces hoje em padrões de design e metodologias de desenvolvimento.

292 BilltheLizard Dec 21 2008 at 08:35

O exemplo específico que usei para dar aos alunos é que eles deveriam escrever

List myList = new ArrayList(); // programming to the List interface

ao invés de

ArrayList myList = new ArrayList(); // this is bad

Eles têm exatamente a mesma aparência em um programa curto, mas se você continuar a usar myList100 vezes em seu programa, poderá começar a ver a diferença. A primeira declaração garante que você apenas chame métodos myListdefinidos pela Listinterface (portanto, nenhum ArrayListmétodo específico). Se você programou para a interface desta forma, mais tarde você pode decidir que você realmente precisa

List myList = new TreeList();

e você só precisa alterar seu código naquele local. Você já sabe que o resto do seu código não faz nada que será quebrado ao alterar a implementação porque você programou para a interface .

Os benefícios são ainda mais óbvios (eu acho) quando você está falando sobre parâmetros de método e valores de retorno. Veja isso por exemplo:

public ArrayList doSomething(HashMap map);

Essa declaração de método liga você a duas implementações concretas ( ArrayListe HashMap). Assim que esse método é chamado de outro código, qualquer alteração nesses tipos provavelmente significa que você também terá que alterar o código de chamada. Seria melhor programar para as interfaces.

public List doSomething(Map map);

Agora não importa que tipo de Listvocê retorna, ou que tipo de Mapé passado como um parâmetro. As alterações feitas dentro do doSomethingmétodo não o forçarão a alterar o código de chamada.

75 kdgregory Dec 21 2008 at 07:59

Programar para uma interface é dizer: "Preciso dessa funcionalidade e não me importo de onde ela vem."

Considere (em Java), a Listinterface de contra os ArrayListe LinkedListconcretas classes. Se tudo que me importa é que tenho uma estrutura de dados contendo vários itens de dados que devo acessar por meio de iteração, eu escolheria um List(e isso é 99% das vezes). Se eu sei que preciso inserir / excluir em tempo constante de qualquer uma das extremidades da lista, posso escolher a LinkedListimplementação concreta (ou, mais provavelmente, usar a interface Queue ). Se eu sei que preciso de acesso aleatório por índice, escolho a ArrayListclasse concreta.

39 JonathanAllen Jul 20 2012 at 02:34

Programar para uma interface não tem absolutamente nada a ver com interfaces abstratas como vemos em Java ou .NET. Não é nem mesmo um conceito OOP.

O que isso significa é não mexer nos detalhes internos de um objeto ou estrutura de dados. Use o Abstract Program Interface, ou API, para interagir com seus dados. Em Java ou C #, isso significa usar propriedades e métodos públicos em vez de acesso a campos brutos. Para C, isso significa usar funções em vez de ponteiros brutos.

EDITAR: E com bancos de dados, isso significa usar visualizações e procedimentos armazenados em vez de acesso direto à tabela.

38 tvanfosson Dec 21 2008 at 08:20

Usar interfaces é um fator chave para tornar seu código facilmente testável, além de remover acoplamentos desnecessários entre suas classes. Ao criar uma interface que define as operações em sua classe, você permite que as classes que desejam usar essa funcionalidade possam usá-la sem depender diretamente de sua classe de implementação. Se, posteriormente, você decidir alterar e usar uma implementação diferente, precisará apenas alterar a parte do código onde a implementação é instanciada. O resto do código não precisa ser alterado porque depende da interface, não da classe de implementação.

Isso é muito útil na criação de testes de unidade. Na classe em teste, você depende da interface e injeta uma instância da interface na classe (ou uma fábrica que permite construir instâncias da interface conforme necessário) por meio do construtor ou um settor de propriedade. A classe usa a interface fornecida (ou criada) em seus métodos. Ao escrever seus testes, você pode simular ou simular a interface e fornecer uma interface que responda com os dados configurados em seu teste de unidade. Você pode fazer isso porque sua classe em teste lida apenas com a interface, não com sua implementação concreta. Qualquer classe que implemente a interface, incluindo sua classe simulada ou falsa, servirá.

EDIT: Abaixo está um link para um artigo onde Erich Gamma discute sua citação, "Programe para uma interface, não uma implementação."

http://www.artima.com/lejava/articles/designprinciples.html

36 LasseV.Karlsen Dec 21 2008 at 07:58

Você deve olhar para Inversão de Controle:

  • Martin Fowler: Inversão de recipientes de controle e o padrão de injeção de dependência
  • Wikipedia: Inversão de controle

Em tal cenário, você não escreveria isto:

IInterface classRef = new ObjectWhatever();

Você escreveria algo assim:

IInterface classRef = container.Resolve<IInterface>();

Isso iria para uma configuração baseada em regras no containerobjeto e construiria o objeto real para você, que poderia ser ObjectWhatever. O importante é que você poderia substituir essa regra por algo que usasse outro tipo de objeto, e seu código ainda funcionaria.

Se deixarmos o IoC fora da mesa, você pode escrever um código que sabe que pode se comunicar com um objeto que faz algo específico , mas não com que tipo de objeto ou como o faz.

Isso seria útil ao passar parâmetros.

Quanto à sua pergunta entre parênteses "Além disso, como você poderia escrever um método que leva em um objeto que implementa uma Interface? Isso é possível?", Em C # você simplesmente usaria o tipo de interface para o tipo de parâmetro, assim:

public void DoSomethingToAnObject(IInterface whatever) { ... }

Isso se conecta diretamente ao "falar com um objeto que faz algo específico". O método definido acima sabe o que esperar do objeto, que implementa tudo em IInterface, mas não se importa com que tipo de objeto seja, apenas que adere ao contrato, que é o que é uma interface.

Por exemplo, você provavelmente está familiarizado com calculadoras e provavelmente já usou algumas em seus dias, mas na maioria das vezes são todas diferentes. Você, por outro lado, sabe como uma calculadora padrão deve funcionar, então você pode usar todas elas, mesmo se você não puder usar os recursos específicos que cada calculadora tem que nenhuma outra possui.

Essa é a beleza das interfaces. Você pode escrever um trecho de código, que sabe que obterá objetos passados ​​a ele dos quais pode esperar determinado comportamento. Não importa o tipo de objeto que seja, apenas que suporta o comportamento necessário.

Deixe-me dar um exemplo concreto.

Temos um sistema de tradução personalizado para formulários do Windows. Este sistema percorre os controles em um formulário e traduz o texto em cada um. O sistema sabe como lidar com controles básicos, como o-tipo-de-controle-que-tem-uma-propriedade-de-texto e coisas básicas semelhantes, mas para qualquer coisa básica, fica aquém.

Agora, uma vez que os controles herdam de classes predefinidas sobre as quais não temos controle, poderíamos fazer uma das três coisas:

  1. Construir suporte para nosso sistema de tradução para detectar especificamente com qual tipo de controle ele está trabalhando e traduzir os bits corretos (pesadelo de manutenção)
  2. Construir suporte em classes básicas (impossível, uma vez que todos os controles herdam de diferentes classes predefinidas)
  3. Adicionar suporte de interface

Então, fizemos nr. 3. Todos os nossos controles implementam ILocalizable, que é uma interface que nos dá um método, a capacidade de traduzir "a si mesmo" em um contêiner de texto / regras de tradução. Como tal, o formulário não precisa saber que tipo de controle ele encontrou, apenas que ele implementa a interface específica e sabe que existe um método onde ele pode chamar para localizar o controle.

30 BillRosmus Apr 15 2016 at 02:36

Código para a interface Não a implementação não tem NADA a ver com Java, nem sua construção de interface.

Esse conceito ganhou destaque nos livros Patterns / Gang of Four, mas provavelmente já existia bem antes disso. O conceito certamente existia bem antes de Java existir.

A construção de interface Java foi criada para ajudar nessa ideia (entre outras coisas), e as pessoas se tornaram muito focadas na construção como o centro do significado, em vez de na intenção original. No entanto, é a razão pela qual temos métodos e atributos públicos e privados em Java, C ++, C #, etc.

Significa apenas interagir com um objeto ou interface pública do sistema. Não se preocupe ou mesmo antecipe como ele faz o que faz internamente. Não se preocupe em como isso é implementado. No código orientado a objetos, é por isso que temos métodos / atributos públicos e privados. Pretendemos usar os métodos públicos porque os métodos privados existem apenas para uso interno, dentro da classe. Eles constituem a implementação da classe e podem ser alterados conforme necessário, sem alterar a interface pública. Suponha que, em relação à funcionalidade, um método em uma classe executará a mesma operação com o mesmo resultado esperado toda vez que você chamá-lo com os mesmos parâmetros. Permite ao autor mudar o funcionamento da aula, sua implementação, sem quebrar a forma como as pessoas interagem com ela.

E você pode programar para a interface, não para a implementação, sem nunca usar uma construção de Interface. Você pode programar para a interface, não a implementação em C ++, que não tem uma construção de Interface. Você pode integrar dois sistemas corporativos massivos de forma muito mais robusta, desde que eles interajam por meio de interfaces públicas (contratos), em vez de chamar métodos em objetos internos aos sistemas. Espera-se que as interfaces sempre reajam da mesma maneira esperada, dados os mesmos parâmetros de entrada; se implementado na interface e não na implementação. O conceito funciona em muitos lugares.

Esqueça o pensamento de que as interfaces Java têm qualquer coisa a ver com o conceito de 'Programa para a interface, não a implementação'. Eles podem ajudar a aplicar o conceito, mas eles são não o conceito.

14 ToddSmith Dec 21 2008 at 11:45

Parece que você entende como as interfaces funcionam, mas não tem certeza de quando usá-las e quais vantagens elas oferecem. Aqui estão alguns exemplos de quando uma interface faria sentido:

// if I want to add search capabilities to my application and support multiple search
// engines such as Google, Yahoo, Live, etc.

interface ISearchProvider
{
    string Search(string keywords);
}

então eu poderia criar GoogleSearchProvider, YahooSearchProvider, LiveSearchProvider, etc.

// if I want to support multiple downloads using different protocols
// HTTP, HTTPS, FTP, FTPS, etc.
interface IUrlDownload
{
    void Download(string url)
}

// how about an image loader for different kinds of images JPG, GIF, PNG, etc.
interface IImageLoader
{
    Bitmap LoadImage(string filename)
}

em seguida, crie JpegImageLoader, GifImageLoader, PngImageLoader, etc.

A maioria dos sistemas de suplementos e plugins funcionam com interfaces.

Outro uso popular é para o padrão Repositório. Digamos que eu queira carregar uma lista de CEPs de diferentes fontes

interface IZipCodeRepository
{
    IList<ZipCode> GetZipCodes(string state);
}

em seguida, poderia criar um XMLZipCodeRepository, SQLZipCodeRepository, CSVZipCodeRepository, etc. Para meus aplicativos da web, geralmente crio repositórios XML no início para que possa colocar algo em funcionamento antes que o banco de dados SQL esteja pronto. Assim que o banco de dados estiver pronto, escrevo um SQLRepository para substituir a versão XML. O resto do meu código permanece inalterado, uma vez que é executado exclusivamente a partir de interfaces.

Os métodos podem aceitar interfaces como:

PrintZipCodes(IZipCodeRepository zipCodeRepository, string state)
{
    foreach (ZipCode zipCode in zipCodeRepository.GetZipCodes(state))
    {
        Console.WriteLine(zipCode.ToString());
    }
}
10 EdS. Dec 21 2008 at 08:07

Isso torna seu código muito mais extensível e fácil de manter quando você tem conjuntos de classes semelhantes. Eu sou um programador júnior, então não sou um especialista, mas acabei de terminar um projeto que exigia algo semelhante.

Eu trabalho com um software do lado do cliente que se comunica com um servidor que executa um dispositivo médico. Estamos desenvolvendo uma nova versão deste dispositivo que possui alguns novos componentes que o cliente deve configurar ocasionalmente. Existem dois tipos de novos componentes, e são diferentes, mas também muito semelhantes. Basicamente, tive que criar dois formulários de configuração, duas classes de listas, dois de tudo.

Decidi que seria melhor criar uma classe base abstrata para cada tipo de controle que manteria quase toda a lógica real e, em seguida, os tipos derivados para cuidar das diferenças entre os dois componentes. No entanto, as classes básicas não seriam capazes de realizar operações nesses componentes se eu tivesse que me preocupar com os tipos o tempo todo (bem, eles poderiam, mas haveria uma instrução "if" ou switch em cada método) .

Eu defini uma interface simples para esses componentes e todas as classes base se comunicam com essa interface. Agora, quando eu mudo algo, ele praticamente 'funciona' em qualquer lugar e não tenho duplicação de código.

10 Jatin Apr 15 2013 at 20:05

Muita explicação por aí, mas para torná-la ainda mais simples. Tomemos, por exemplo, a List. Pode-se implementar uma lista com:

  1. Uma matriz interna
  2. Uma lista ligada
  3. Outras implementações

Ao construir para uma interface, digamos a List. Você apenas codifica quanto à definição de Lista ou o que Listsignifica na realidade.

Você pode usar qualquer tipo de implementação internamente, digamos, uma arrayimplementação. Mas suponha que você deseja alterar a implementação por algum motivo, digamos, um bug ou desempenho. Depois, basta alterar a declaração List<String> ls = new ArrayList<String>()para List<String> ls = new LinkedList<String>().

Em nenhum outro lugar do código, você terá que alterar qualquer outra coisa; Porque todo o resto foi construído na definição de List.

8 KevinLe-Khnle Dec 21 2008 at 08:20

Se você programa em Java, o JDBC é um bom exemplo. JDBC define um conjunto de interfaces, mas não diz nada sobre a implementação. Seus aplicativos podem ser escritos neste conjunto de interfaces. Em teoria, você escolhe algum driver JDBC e seu aplicativo simplesmente funcionaria. Se você descobrir que há um driver JDBC mais rápido, "melhor" ou mais barato ou por qualquer motivo, você pode, em teoria, reconfigurar seu arquivo de propriedade e, sem ter que fazer nenhuma alteração em seu aplicativo, ele ainda funcionará.

8 dbones Dec 21 2008 at 08:52

Programar para interfaces é incrível, promove um acoplamento fraco. Como @lassevk mencionou, Inversão de Controle é um ótimo uso disso.

Além disso, observe os princípios SOLID . aqui está uma série de vídeos

Ele passa por um código rígido (exemplo fortemente acoplado), em seguida, examina as interfaces, finalmente progredindo para uma ferramenta IoC / DI (NInject)

7 nonopolarity Jan 01 2016 at 23:00

Cheguei tarde a esta questão, mas quero mencionar aqui que a linha "Programa para uma interface, não uma implementação" teve uma boa discussão no livro GoF (Gang of Four) Design Patterns.

Afirmou, na pág. 18:

Programa para uma interface, não uma implementação

Não declare variáveis ​​como instâncias de classes concretas particulares. Em vez disso, comprometa-se apenas com uma interface definida por uma classe abstrata. Você descobrirá que este é um tema comum dos padrões de projeto neste livro.

e acima disso, começou com:

Existem dois benefícios em manipular objetos apenas em termos da interface definida por classes abstratas:

  1. Os clientes permanecem inconscientes dos tipos específicos de objetos que usam, desde que os objetos sigam a interface que os clientes esperam.
  2. Os clientes permanecem alheios às classes que implementam esses objetos. Os clientes sabem apenas sobre as classes abstratas que definem a interface.

Em outras palavras, não escreva suas classes de forma que tenha um quack()método para patos e, em seguida, um bark()método para cães, porque eles são muito específicos para uma implementação particular de uma classe (ou subclasse). Em vez disso, escreva o método usando nomes que sejam gerais o suficiente para serem usados ​​na classe base, como giveSound()ou move(), para que possam ser usados ​​para patos, cachorros ou até mesmo carros, e então o cliente de suas classes pode apenas dizer em .giveSound()vez de pensando em usar quack()ou bark()mesmo determinar o tipo antes de emitir a mensagem correta a ser enviada ao objeto.

6 whaley Jan 09 2009 at 04:32

Além da resposta já selecionada (e das várias postagens informativas aqui), eu recomendo fortemente pegar uma cópia de Head First Design Patterns . É uma leitura muito fácil e responderá sua pergunta diretamente, explicará por que é importante e mostrará muitos padrões de programação que você pode usar para fazer uso desse princípio (e outros).

5 e11s Dec 21 2008 at 08:08

Para adicionar às postagens existentes, às vezes a codificação para interfaces ajuda em grandes projetos quando os desenvolvedores trabalham em componentes separados simultaneamente. Tudo que você precisa é definir as interfaces antecipadamente e escrever o código para elas enquanto outros desenvolvedores escrevem o código para a interface que você está implementando.

5 ShaunLuttin Jul 04 2017 at 02:36

Pode ser vantajoso programar para interfaces, mesmo quando não dependemos de abstrações.

A programação para interfaces nos força a usar um subconjunto contextualmente apropriado de um objeto . Isso ajuda porque:

  1. nos impede de fazer coisas contextualmente inadequadas, e
  2. nos permite mudar com segurança a implementação no futuro.

Por exemplo, considere uma Personclasse que implementa o Friende a Employeeinterface.

class Person implements AbstractEmployee, AbstractFriend {
}

No contexto do aniversário da pessoa, programamos para a Friendinterface, para evitar tratar a pessoa como um Employee.

function party() {
    const friend: Friend = new Person("Kathryn");
    friend.HaveFun();
}

No contexto do trabalho da pessoa, programamos para a Employeeinterface, para evitar confundir os limites do local de trabalho.

function workplace() {
    const employee: Employee = new Person("Kathryn");
    employee.DoWork();
}

Excelente. Temos nos comportado de maneira adequada em diferentes contextos e nosso software está funcionando bem.

No futuro, se nosso negócio mudar para trabalhar com cães, poderemos mudar o software com bastante facilidade. Primeiro, criamos uma Dogclasse que implementa ambos Friende Employee. Então, mudamos new Person()com segurança para new Dog(). Mesmo que ambas as funções tenham milhares de linhas de código, essa edição simples funcionará porque sabemos que o seguinte é verdade:

  1. A função partyusa apenas o Friendsubconjunto de Person.
  2. A função workplaceusa apenas o Employeesubconjunto de Person.
  3. A classe Dogimplementa as interfaces Friende Employee.

Por outro lado, se um partyou workplacese tivesse programado contra Person, haveria o risco de ambos terem um Personcódigo específico. Mudar de Personpara Dogexigiria que vasculhemos o código para extirpar qualquer Personcódigo específico que Dognão seja compatível.

Moral da história : programar para interfaces ajuda nosso código a se comportar de maneira adequada e a estar pronto para mudanças. Também prepara nosso código para depender de abstrações, o que traz ainda mais vantagens.

4 Richard Jan 09 2009 at 04:12

Também é bom para testes de unidade, você pode injetar suas próprias classes (que atendem aos requisitos da interface) em uma classe que depende dela

4 AbhishekShrivastava Apr 28 2010 at 23:44

Se estou escrevendo uma nova classe Swimmerpara adicionar a funcionalidade swim()e preciso usar um objeto de classe Dog, digamos , e essa Dogclasse implementa a interface Animalque declara swim().

No topo da hierarquia ( Animal), é muito abstrato, enquanto na parte inferior ( Dog) é muito concreto. A maneira como penso em "programar para interfaces" é que, conforme escrevo a Swimmeraula, quero escrever meu código na interface que está tão acima na hierarquia que, neste caso, é um Animalobjeto. Uma interface é livre de detalhes de implementação e, portanto, torna seu código fracamente acoplado.

Os detalhes de implementação podem ser alterados com o tempo, no entanto, isso não afetaria o código restante, uma vez que você está apenas interagindo com a interface e não com a implementação. Você não se importa como é a implementação ... tudo o que você sabe é que haverá uma classe que implementará a interface.

3 Damien Dec 22 2008 at 05:05

Então, apenas para fazer isso direito, a vantagem de uma interface é que posso separar a chamada de um método de qualquer classe particular. Em vez disso, crie uma instância da interface, onde a implementação é dada de qualquer classe que eu escolher que implemente essa interface. Permitindo-me assim ter muitas classes, que têm funcionalidades semelhantes, mas ligeiramente diferentes e em alguns casos (os casos relacionados com a intenção da interface) não me importam com qual objeto ela é.

Por exemplo, eu poderia ter uma interface de movimento. Um método que faz algo 'se mover' e qualquer objeto (Pessoa, Carro, Gato) que implementa a interface de movimento pode ser passado e instruído a se mover. Sem o método, todos sabem o tipo de aula que é.

3 DanielEarwicker Dec 21 2008 at 08:46

Imagine que você tem um produto chamado 'Zebra' que pode ser estendido por plug-ins. Ele encontra os plug-ins procurando DLLs em algum diretório. Ele carrega todas essas DLLs e usa reflexão para localizar quaisquer classes que as implementam IZebraPlugine, em seguida, chama os métodos dessa interface para se comunicar com os plug-ins.

Isso o torna completamente independente de qualquer classe de plugin específica - não importa quais sejam as classes. Ele só se preocupa em atender às especificações da interface.

As interfaces são uma forma de definir pontos de extensibilidade como este. O código que se comunica com uma interface é mais fracamente acoplado - na verdade, não está acoplado a nenhum outro código específico. Ele pode interagir com plug-ins escritos anos depois por pessoas que nunca conheceram o desenvolvedor original.

Em vez disso, você poderia usar uma classe base com funções virtuais - todos os plug-ins seriam derivados da classe base. Mas isso é muito mais limitante porque uma classe pode ter apenas uma classe base, enquanto pode implementar qualquer número de interfaces.

3 TraeBarlow Dec 19 2012 at 05:35

Explicação C ++.

Pense em uma interface como os métodos públicos de suas classes.

Em seguida, você pode criar um modelo que 'dependa' desses métodos públicos para realizar sua própria função (ele faz chamadas de função definidas na interface pública das classes). Digamos que este modelo é um contêiner, como uma classe Vector, e a interface da qual depende é um algoritmo de pesquisa.

Qualquer classe de algoritmo que define as funções / interface Vector faz chamadas para satisfazer o 'contrato' (como alguém explicou na resposta original). Os algoritmos nem precisam ser da mesma classe base; o único requisito é que as funções / métodos dos quais o Vector depende (interface) sejam definidos em seu algoritmo.

O objetivo de tudo isso é que você pode fornecer qualquer algoritmo / classe de pesquisa diferente, desde que forneça a interface da qual o Vector depende (pesquisa de bolha, pesquisa sequencial, pesquisa rápida).

Você também pode querer projetar outros contêineres (listas, filas) que usem o mesmo algoritmo de pesquisa do Vector, fazendo com que cumpram a interface / contrato do qual seus algoritmos de pesquisa dependem.

Isso economiza tempo (princípio OOP 'reutilização de código'), pois você é capaz de escrever um algoritmo uma vez, em vez de repetidamente específico para cada novo objeto que você cria, sem complicar demais o problema com uma árvore de herança superdimensionada.

Quanto a 'perder' como as coisas funcionam; big-time (pelo menos em C ++), pois é assim que a maior parte da estrutura da Biblioteca Padrão TEMPLATE opera.

É claro que, ao usar classes de herança e abstratas, a metodologia de programação para uma interface muda; mas o princípio é o mesmo, suas funções / métodos públicos são a interface de suas classes.

Este é um tópico enorme e um dos princípios básicos dos Design Patterns.

3 RogerV Dec 21 2008 at 11:17

Em Java, todas essas classes concretas implementam a interface CharSequence:

CharBuffer, String, StringBuffer, StringBuilder

Essas classes concretas não têm uma classe pai comum diferente de Object, então não há nada que as relacione, a não ser o fato de que cada uma tem algo a ver com matrizes de caracteres, representando ou manipulando tais. Por exemplo, os caracteres de String não podem ser alterados depois que um objeto String é instanciado, enquanto os caracteres de StringBuffer ou StringBuilder podem ser editados.

No entanto, cada uma dessas classes é capaz de implementar adequadamente os métodos de interface CharSequence:

char charAt(int index)
int length()
CharSequence subSequence(int start, int end)
String toString()

Em alguns casos, as classes da biblioteca de classes Java que costumavam aceitar String foram revisadas para aceitar agora a interface CharSequence. Portanto, se você tiver uma instância de StringBuilder, em vez de extrair um objeto String (o que significa instanciar uma nova instância de objeto), ele pode apenas passar o próprio StringBuilder à medida que implementa a interface CharSequence.

A interface Appendable que algumas classes implementam tem praticamente o mesmo tipo de benefício para qualquer situação em que os caracteres possam ser anexados a uma instância da instância do objeto de classe concreto subjacente. Todas essas classes concretas implementam a interface Appendable:

BufferedWriter, CharArrayWriter, CharBuffer, FileWriter, FilterWriter, LogStream, OutputStreamWriter, PipedWriter, PrintStream, PrintWriter, StringBuffer, StringBuilder, StringWriter, Writer

3 SanjayRabari Aug 08 2014 at 17:25

Conto: pede-se a um carteiro que volte para casa depois de casa e receba as capas que contém (cartas, documentos, cheques, vales-presente, inscrição, carta de amor) com o endereço escrito para entrega.

Suponha que não haja cobertura e peça ao carteiro para ir para casa depois de casa e receber todas as coisas e entregar para outras pessoas, o carteiro pode ficar confuso.

Então é melhor embrulhar com uma capa (em nossa história é a interface), então ele fará seu trabalho bem.

Agora a função do carteiro é receber e entregar apenas as capas (ele não se importaria com o que está dentro da capa).

Crie um tipo de tipo interfacenão real, mas implemente-o com o tipo real.

Criar a interface significa que seus componentes se encaixam no resto do código facilmente

Eu dou um exemplo.

você tem a interface AirPlane conforme abaixo.

interface Airplane{
    parkPlane();
    servicePlane();
}

Suponha que você tenha métodos em sua classe Controller de aviões, como

parkPlane(Airplane plane)

e

servicePlane(Airplane plane)

implementado em seu programa. Isso não QUEBRARÁ seu código. Quer dizer, não precisa mudar, desde que aceite argumentos como AirPlane.

Porque ele vai aceitar qualquer avião apesar tipo real, flyer, highflyr, fighter, etc.

Além disso, em uma coleção:

List<Airplane> plane; // Vai levar todos os seus aviões.

O exemplo a seguir esclarecerá sua compreensão.


Você tem um avião de combate que o implementa, então

public class Fighter implements Airplane {

    public void  parkPlane(){
        // Specific implementations for fighter plane to park
    }
    public void  servicePlane(){
        // Specific implementatoins for fighter plane to service.
    }
}

A mesma coisa para HighFlyer e outras classes:

public class HighFlyer implements Airplane {

    public void  parkPlane(){
        // Specific implementations for HighFlyer plane to park
    }

    public void  servicePlane(){
        // specific implementatoins for HighFlyer plane to service.
    }
}

Agora pense que suas classes de controlador usando AirPlanevárias vezes,

Suponha que sua classe Controller seja ControlPlane como abaixo,

public Class ControlPlane{ 
 AirPlane plane;
 // so much method with AirPlane reference are used here...
}

Aí vem a mágica, pois você pode criar suas novas AirPlaneinstâncias de tipo quantas desejar e não está alterando o código de ControlPlaneclasse.

Você pode adicionar uma instância ...

JumboJetPlane // implementing AirPlane interface.
AirBus        // implementing AirPlane interface.

Você também pode remover instâncias de tipos criados anteriormente.

2 ShivamSugandhi May 27 2016 at 17:44

Uma interface é como um contrato, onde você deseja que sua classe de implementação implemente métodos escritos no contrato (interface). Como o Java não fornece herança múltipla, "programar para interface" é uma boa maneira de obter herança múltipla.

Se você tem uma classe A que já está estendendo alguma outra classe B, mas deseja que essa classe A também siga certas diretrizes ou implemente um determinado contrato, você pode fazer isso pela estratégia de "programação para interface".

2 Noname Mar 09 2014 at 13:17

P: - ... "Você poderia usar alguma classe que implemente uma interface?"
R: - Sim.

P: - ... "Quando você precisa fazer isso?"
R: - Cada vez que você precisa de uma (s) classe (s) que implemente (m) interface (s).

Nota: Não foi possível instanciar uma interface não implementada por uma classe - True.

  • Porque?
  • Porque a interface tem apenas protótipos de método, não definições (apenas nomes de funções, não sua lógica)

AnIntf anInst = new Aclass();
// poderíamos fazer isso apenas se Aclass implementasse AnIntf.
// anInst terá referência Aclass.


Nota: agora podemos entender o que aconteceria se Bclass e Cclass implementassem o mesmo Dintf.

Dintf bInst = new Bclass();  
// now we could call all Dintf functions implemented (defined) in Bclass.

Dintf cInst = new Cclass();  
// now we could call all Dintf functions implemented (defined) in Cclass.

O que temos: Mesmos protótipos de interface (nomes de funções na interface) e chamam diferentes implementações.

Bibliografia: Protótipos - wikipedia

1 Ravindrababu Dec 27 2016 at 23:29

O programa para uma interface permite alterar perfeitamente a implementação do contrato definido pela interface. Ele permite um acoplamento fraco entre o contrato e as implementações específicas.

IInterface classRef = new ObjectWhatever()

Você poderia usar qualquer classe que implemente IInterface? Quando você precisa fazer isso?

Dê uma olhada nesta pergunta SE como um bom exemplo.

Por que a interface para uma classe Java deve ser preferida?

o uso de uma Interface atinge o desempenho?

Se sim, quanto?

sim. Ele terá uma pequena sobrecarga de desempenho em sub-segundos. Mas se o seu aplicativo tiver necessidade de alterar a implementação da interface dinamicamente, não se preocupe com o impacto no desempenho.

como você pode evitá-lo sem ter que manter dois bits de código?

Não tente evitar várias implementações de interface se seu aplicativo precisar delas. Na ausência de forte acoplamento de interface com uma implementação específica, você pode ter que implantar o patch para alterar uma implementação para outra implementação.

Um bom caso de uso: Implementação do padrão de estratégia:

Exemplo do mundo real do padrão de estratégia

MichelKeijzers Dec 18 2014 at 20:23

Também vejo muitas respostas boas e explicativas aqui, então quero dar meu ponto de vista aqui, incluindo algumas informações extras que percebi ao usar este método.

Teste de unidade

Nos últimos dois anos, escrevi um projeto de hobby e não escrevi testes de unidade para ele. Depois de escrever cerca de 50 mil linhas, descobri que seria realmente necessário escrever testes de unidade. Não usei interfaces (ou com muita moderação) ... e quando fiz meu primeiro teste unitário, descobri que era complicado. Porque?

Porque eu tive que fazer muitas instâncias de classe, usadas para entrada como variáveis ​​e / ou parâmetros de classe. Portanto, os testes se parecem mais com testes de integração (tendo que fazer uma 'estrutura' completa de classes, uma vez que tudo foi interligado).

Medo de interfaces Então decidi usar interfaces. Meu medo era que eu tivesse que implementar todas as funcionalidades em todos os lugares (em todas as classes usadas) várias vezes. De certa forma, isso é verdade, no entanto, usando a herança, isso pode ser bastante reduzido.

Combinação de interfaces e herança Descobri que a combinação é muito boa para ser usada. Dou um exemplo muito simples.

public interface IPricable
{
    int Price { get; }
}

public interface ICar : IPricable

public abstract class Article
{
    public int Price { get { return ... } }
}

public class Car : Article, ICar
{
    // Price does not need to be defined here
}

Desta forma, não é necessário copiar o código, tendo ainda o benefício de usar um carro como interface (ICar).

RobertRocha Aug 06 2015 at 06:48

Vamos começar com algumas definições primeiro:

Interface n. O conjunto de todas as assinaturas definidas pelas operações de um objeto é chamado de interface para o objeto

Digite n. Uma interface particular

Um exemplo simples de uma interface de , tal como definido acima seria todos os métodos DOP de objectos, tais como query(), commit(), close()etc., como um todo, não separadamente. Esses métodos, ou seja, sua interface definem o conjunto completo de mensagens, solicitações que podem ser enviadas ao objeto.

Um tipo conforme definido acima é uma interface específica. I vai utilizar a interface de forma feita para demonstrar-se: draw(), getArea(), getPerimeter()etc ..

Se um objeto é do tipo Banco de Dados, significa que ele aceita mensagens / solicitações da interface do banco de dados query(), commit()etc. Os objetos podem ser de vários tipos. Você pode ter um objeto de banco de dados do tipo forma, desde que implemente sua interface; nesse caso, isso seria uma subtipulação .

Muitos objetos podem ser de muitas interfaces / tipos diferentes e implementar essa interface de maneira diferente. Isso nos permite substituir objetos, permitindo-nos escolher qual usar. Também conhecido como polimorfismo.

O cliente só terá conhecimento da interface e não da implementação.

Então, em essência, a programação para uma interface envolveria fazer algum tipo de classe abstrata, como Shapecom a interface apenas especificada draw(), ou seja getCoordinates(), getArea()etc. E, em seguida, fazer com que diferentes classes concretas implementassem essas interfaces, como uma classe Circle, uma classe Square, uma classe Triangle. Portanto, programe para uma interface, não para uma implementação.

ndoty Jul 30 2018 at 22:55

"Programa para interface" significa não fornecer código rígido da maneira certa, o que significa que seu código deve ser estendido sem quebrar a funcionalidade anterior. Apenas extensões, não editando o código anterior.

j2emanue Feb 02 2020 at 15:07

programa para uma interface é um termo do livro GOF. eu não diria diretamente que tem a ver com a interface java, mas sim com interfaces reais. para conseguir uma separação de camada limpa, você precisa criar alguma separação entre os sistemas, por exemplo: Digamos que você tenha um banco de dados concreto que deseja usar, você nunca "programa para o banco de dados", em vez disso, "programa para a interface de armazenamento". Da mesma forma, você nunca "programa para um serviço da Web", mas sim para uma "interface de cliente". isso é para que você possa trocar as coisas facilmente.

Acho que essas regras me ajudam:

1 . usamos uma interface java quando temos vários tipos de um objeto. se eu só tenho um único objeto, não vejo o ponto. se houver pelo menos duas implementações concretas de alguma ideia, então eu usaria uma interface java.

2 . se, como afirmei acima, você deseja trazer a dissociação de um sistema externo (sistema de armazenamento) para seu próprio sistema (banco de dados local), então também use uma interface.

observe como há duas maneiras de considerar quando usá-los. espero que isto ajude.