O motivo das interfaces realmente me ilude. Pelo que entendi, é uma espécie de solução alternativa para a herança múltipla inexistente que não existe em C # (ou assim me disseram).
Tudo o que vejo é que você pré-define alguns membros e funções, que precisam ser redefinidos na classe novamente. Tornando a interface redundante. Parece sintático ... bem, lixo para mim (por favor, sem querer ofender. Lixo como em coisas inúteis).
No exemplo abaixo, extraído de um thread de interfaces C # diferente no estouro de pilha, eu apenas criaria uma classe base chamada Pizza, em vez de uma interface.
exemplo fácil (extraído de uma contribuição diferente de estouro de pilha)
public interface IPizza
{
public void Order();
}
public class PepperoniPizza : IPizza
{
public void Order()
{
//Order Pepperoni pizza
}
}
public class HawaiiPizza : IPizza
{
public void Order()
{
//Order HawaiiPizza
}
}
struct
tipos.Respostas:
O ponto é que a interface representa um contrato . Um conjunto de métodos públicos que qualquer classe de implementação precisa ter. Tecnicamente, a interface governa apenas a sintaxe, ou seja, quais métodos existem, quais argumentos eles obtêm e o que retornam. Geralmente eles também encapsulam a semântica, embora isso seja apenas por documentação.
Você pode ter diferentes implementações de uma interface e trocá-las à vontade. No seu exemplo, como cada instância de pizza é uma,
IPizza
você pode usá-laIPizza
sempre que manipular uma instância de um tipo desconhecido de pizza. Qualquer instância cujo tipo herdaIPizza
é garantida como ordenável, pois possui umOrder()
métodoPython não é estaticamente digitado, portanto, os tipos são mantidos e pesquisados em tempo de execução. Então você pode tentar chamar um
Order()
método em qualquer objeto. O tempo de execução é feliz desde que o objeto possua esse método e provavelmente apenas dê de ombros e diga "Meh.", Se não tiver. Não é assim em c #. O compilador é responsável por fazer as chamadas corretas e, se houver alguma aleatória,object
o compilador ainda não sabe se a instância durante o tempo de execução terá esse método. Do ponto de vista do compilador, é inválido, pois não pode ser verificado. (Você pode fazer essas coisas com reflexão oudynamic
palavra chave, mas isso está indo um pouco longe agora, eu acho.)Observe também que uma interface no sentido usual não precisa necessariamente ser um C #
interface
; pode ser também uma classe abstrata ou mesmo uma classe normal (o que pode ser útil se todas as subclasses precisarem compartilhar algum código comum - na maioria dos casos , no entanto,interface
é suficiente).fonte
foo
implementá-loIWidget
, um programador que estiver vendo uma chamadafoo.woozle()
poderá ver a documentaçãoIWidget
e saber o que esse método deve fazer . O programador pode não ter como saber de onde virá o código para a implementação real, mas qualquer tipo que cumpra oIWidget
contrato de interface será implementado defoo
maneira consistente com esse contrato. Em uma linguagem dinâmica, por outro lado, não haveria um ponto de referência claro para o quefoo.woozle()
deveria significar.Ninguém realmente explicou em termos simples como as interfaces são úteis, por isso vou tentar (e roubar uma idéia da resposta de Shamim um pouco).
Vamos ter a idéia de um serviço de pedidos de pizza. Você pode ter vários tipos de pizzas e uma ação comum para cada pizza está preparando o pedido no sistema. Cada pizza deve ser preparada, mas cada pizza é preparada de maneira diferente . Por exemplo, quando uma pizza de massa recheada é solicitada, o sistema provavelmente deve verificar se certos ingredientes estão disponíveis no restaurante e deixar de lado aqueles que não são necessários para pizzas de prato fundo.
Ao escrever isso no código, tecnicamente você pode fazer
No entanto, as pizzas de prato fundo (em termos de C #) podem exigir propriedades diferentes para serem definidas no
Prepare()
método que a massa recheada e, portanto, você acaba com muitas propriedades opcionais, e a classe não escala bem (e se você adicionar novas tipos de pizza).A maneira correta de resolver isso é usar a interface. A interface declara que todas as pizzas podem ser preparadas, mas cada pizza pode ser preparada de maneira diferente. Portanto, se você possui as seguintes interfaces:
Agora, o código de processamento de pedidos não precisa saber exatamente quais tipos de pizzas foram encomendados para manipular os ingredientes. Apenas tem:
Mesmo que cada tipo de pizza seja preparado de maneira diferente, essa parte do código não precisa se preocupar com o tipo de pizza com que estamos lidando, apenas sabe que está sendo chamada para pizzas e, portanto, cada chamada para
Prepare
preparar automaticamente cada pizza corretamente com base em seu tipo, mesmo que a coleção possua vários tipos de pizzas.fonte
public
. Portanto, dentro da interface, deve ser apenasvoid Prepare();
Para mim, quando eu comecei, o ponto para isso ficou claro quando você parava de considerá-los coisas para tornar seu código mais fácil / rápido de escrever - esse não é o objetivo deles. Eles têm vários usos:
(Isso vai perder a analogia da pizza, pois não é muito fácil visualizar um uso disso)
Digamos que você esteja criando um jogo simples na tela e ele terá criaturas com as quais você interage.
R: Eles podem tornar seu código mais fácil de manter no futuro, introduzindo um acoplamento flexível entre o front-end e a implementação do back-end.
Você pode escrever isso para começar, pois só haverá trolls:
A parte dianteira:
Duas semanas depois, o marketing decide que você também precisa de Orcs, como eles leram sobre eles no twitter, então você teria que fazer algo como:
A parte dianteira:
E você pode ver como isso começa a ficar confuso. Você pode usar uma interface aqui para que seu front-end seja gravado uma vez e (aqui está a parte importante) testado e, em seguida, você poderá conectar outros itens de back-end conforme necessário:
Front end é então:
O front end agora se preocupa apenas com a interface ICreature - não se preocupa com a implementação interna de um troll ou um orc, mas apenas com o fato de eles implementarem o ICreature.
Um ponto importante a ser observado ao olhar para esse ponto de vista é que você também poderia facilmente ter usado uma classe de criatura abstrata e, dessa perspectiva, isso tem o mesmo efeito.
E você pode extrair a criação para uma fábrica:
E nosso front end se tornaria:
O front end agora nem precisa ter uma referência à biblioteca onde Troll e Orc são implementados (desde que a fábrica esteja em uma biblioteca separada) - ele não precisa saber nada sobre eles.
B: Digamos que você tenha uma funcionalidade que apenas algumas criaturas terão em sua estrutura de dados homogênea , por exemplo
O front end pode então ser:
C: Uso para injeção de dependência
A maioria das estruturas de injeção de dependência é mais fácil de trabalhar quando há um acoplamento muito frouxo entre o código do front-end e a implementação do back-end. Se pegarmos nosso exemplo de fábrica acima e fazer com que nossa fábrica implemente uma interface:
Nosso front-end pode então ter isso injetado (por exemplo, um controlador de API MVC) através do construtor (normalmente):
Com nossa estrutura de DI (por exemplo, Ninject ou Autofac), podemos configurá-los para que, em tempo de execução, uma instância do CreatureFactory seja criada sempre que um ICreatureFactory for necessário em um construtor - isso torna nosso código simples e agradável.
Isso também significa que, quando escrevemos um teste de unidade para o nosso controlador, podemos fornecer um ICreatureFactory zombado (por exemplo, se a implementação concreta exigir acesso ao banco de dados, não queremos que nossos testes de unidade dependam disso) e testar facilmente o código em nosso controlador .
D: Existem outros usos, por exemplo, você tem dois projetos A e B que, por razões de 'legado', não são bem estruturados e A tem uma referência a B.
Em seguida, você encontra a funcionalidade em B que precisa chamar um método já em A. Você não pode fazê-lo usando implementações concretas ao obter uma referência circular.
Você pode ter uma interface declarada em B que a classe em A implementa. Seu método em B pode receber uma instância de uma classe que implementa a interface sem problemas, mesmo que o objeto concreto seja do tipo A.
fonte
Aqui estão seus exemplos reexplicados:
fonte
Os exemplos acima não fazem muito sentido. Você pode realizar todos os exemplos acima usando classes (classe abstrata se quiser que ele se comporte apenas como um contrato ):
Você obtém o mesmo comportamento da interface. Você pode criar
List<Food>
e iterar sem saber qual classe fica no topo.Um exemplo mais adequado seria herança múltipla:
Em seguida, você pode usar todos eles como
MenuItem
e não se importa com como eles lidam com cada chamada de método.fonte
Explicação simples com analogia
O problema a resolver: Qual é o propósito do polimorfismo?
Analogia: Então, eu sou um indicador em um canteiro de obras.
Comerciantes andam no canteiro de obras o tempo todo. Não sei quem vai passar por essas portas. Mas eu basicamente digo a eles o que fazer.
O problema com a abordagem acima é que eu tenho que: (i) saber quem está entrando naquela porta e, dependendo de quem é, eu tenho que dizer a eles o que fazer. Isso significa que eu tenho que saber tudo sobre um comércio específico. Existem custos / benefícios associados a esta abordagem:
As implicações de saber o que fazer:
Isso significa que se o código do carpinteiro mudar de:
BuildScaffolding()
paraBuildScaffold()
(ou seja, uma ligeira alteração no nome), também terei que alterar também a classe de chamada (ou seja, aForeperson
classe) - você terá que fazer duas alterações no código em vez de (basicamente ) apenas um. Com o polimorfismo, você (basicamente) só precisa fazer uma alteração para obter o mesmo resultado.Em segundo lugar, você não precisará perguntar constantemente: quem é você? ok faça isso ... quem é você? ok, faça isso ..... polimorfismo - seca esse código e é muito eficaz em determinadas situações:
com o polimorfismo, você pode adicionar facilmente classes adicionais de profissionais sem alterar nenhum código existente. (ou seja, o segundo dos princípios de design do SOLID: princípio de abertura / fechamento).
A solução
Imagine um cenário em que, não importa quem entre pela porta, posso dizer: "Work ()" e eles fazem seus trabalhos de respeito em que se especializam: o encanador lidaria com canos e o eletricista com fios.
O benefício dessa abordagem é que: (i) eu não preciso saber exatamente quem está entrando por aquela porta - tudo o que preciso saber é que eles serão um tipo de tradie e que eles podem fazer o trabalho e, em segundo lugar , (ii) não preciso saber nada sobre esse comércio específico. A tradie cuidará disso.
Então, em vez disso:
Eu posso fazer algo assim:
Qual o benefício?
O benefício é que, se os requisitos específicos de trabalho do carpinteiro, etc, mudarem, o indicador não precisará alterar seu código - ele não precisará saber ou se importar. Tudo o que importa é que o carpinteiro saiba o que se entende por Work (). Em segundo lugar, se um novo tipo de trabalhador da construção civil chegar ao local de trabalho, o capataz não precisará saber nada sobre o comércio - todo o capataz se importa se o trabalhador da construção civil (.eg Welder, Glazier, Tiler etc.) puder faça algum trabalho ().
Problema e solução ilustrados (com e sem interfaces):
Sem interface (Exemplo 1):
Sem interface (Exemplo 2):
Com uma interface:
Resumo
Uma interface permite que você faça com que a pessoa faça o trabalho ao qual foi designada, sem que você tenha o conhecimento exato de quem eles são ou as especificidades do que eles podem fazer. Isso permite que você adicione facilmente novos tipos (de comércio) sem alterar o código existente (tecnicamente, você o altera um pouquinho), e esse é o benefício real de uma abordagem OOP versus uma metodologia de programação mais funcional.
Se você não entender alguma das opções acima ou se não estiver claro, pergunte em um comentário e tentarei melhorar a resposta.
fonte
Na ausência de digitação do duck, como você pode usá-lo no Python, o C # depende de interfaces para fornecer abstrações. Se as dependências de uma classe fossem todos tipos concretos, não seria possível transmitir nenhum outro tipo - usando interfaces, você pode transmitir qualquer tipo que implemente a interface.
fonte
O exemplo da Pizza é ruim porque você deve usar uma classe abstrata que lida com a ordem e as pizzas devem substituir o tipo da pizza, por exemplo.
Você usa interfaces quando possui uma propriedade compartilhada, mas suas classes são herdadas de locais diferentes ou quando você não possui nenhum código comum que possa usar. Por exemplo, são usadas coisas que podem ser descartadas
IDisposable
, você sabe que será descartado, você simplesmente não sabe o que acontecerá quando for descartado.Uma interface é apenas um contrato que informa algumas coisas que um objeto pode fazer, quais parâmetros e quais tipos de retorno esperar.
fonte
Considere o caso em que você não controla ou possui as classes base.
Tome controles visuais, por exemplo, no .NET for Winforms todos eles herdam da classe base Control, que é completamente definida na estrutura .NET.
Vamos supor que você esteja no negócio de criar controles personalizados. Você deseja criar novos botões, caixas de texto, listviews, grades, outros enfeites e gostaria que todos tivessem certos recursos exclusivos para o seu conjunto de controles.
Por exemplo, você pode querer uma maneira comum de lidar com eles, ou uma maneira comum de lidar com a localização.
Nesse caso, você não pode "apenas criar uma classe base" porque, se fizer isso, precisará reimplementar tudo o que se relaciona aos controles.
Em vez disso, você descerá de Button, TextBox, ListView, GridView, etc. e adicionará seu código.
Mas isso representa um problema: como você pode agora identificar quais controles são "seus", como criar um código que diz "para todos os controles no formulário que são meus, defina o tema como X".
Digite interfaces.
As interfaces são uma maneira de olhar para um objeto, para determinar se o objeto adere a um determinado contrato.
Você criaria "YourButton", desceria do Button e incluiria suporte para todas as interfaces necessárias.
Isso permitiria escrever código como o seguinte:
Isso não seria possível sem interfaces, em vez disso, você teria que escrever um código como este:
fonte
Nesse caso, você poderia (e provavelmente definiria) apenas definir uma classe base do Pizza e herdá-la. No entanto, existem duas razões pelas quais as interfaces permitem que você faça coisas que não podem ser alcançadas de outras maneiras:
Uma classe pode implementar várias interfaces. Apenas define os recursos que a classe deve ter. Implementar uma variedade de interfaces significa que uma classe pode desempenhar várias funções em locais diferentes.
Uma interface pode ser definida em um escopo maior que a classe ou o chamador. Isso significa que você pode separar a funcionalidade, separar a dependência do projeto e manter a funcionalidade em um projeto ou classe, e a implementação disso em outro lugar.
Uma implicação de 2 é que você pode alterar a classe que está sendo usada, exigindo apenas que ela implemente a interface apropriada.
fonte
Considere que você não pode usar herança múltipla em C # e, em seguida, analise sua pergunta novamente.
fonte
Interface = contrato, usado para acoplamento solto (consulte GRASP ).
fonte
Se estiver trabalhando em uma API para desenhar formas, convém usar chamadas DirectX ou gráficas ou OpenGL. Então, vou criar uma interface que abstraia minha implementação do que você chama.
Então você chamar um método de fábrica:
MyInterface i = MyGraphics.getInstance()
. Então, você tem um contrato para saber em quais funções pode esperarMyInterface
. Portanto, você pode ligari.drawRectangle
oui.drawCube
saber que, se você trocar uma biblioteca por outra, as funções serão suportadas.Isso se torna mais importante se você estiver usando a Dependency Injection, pois, em um arquivo XML, é possível trocar as implementações.
Portanto, você pode ter uma biblioteca de criptografia que pode ser exportada para uso geral e outra que está à venda apenas para empresas americanas, e a diferença é que você altera um arquivo de configuração e o restante do programa não é mudou.
Isso é muito usado com coleções no .NET, pois você deve usar, por exemplo,
List
variáveis e não se preocupe se é um ArrayList ou LinkedList.Enquanto você codifica para a interface, o desenvolvedor pode alterar a implementação real e o restante do programa permanece inalterado.
Isso também é útil no teste de unidade, pois você pode zombar de interfaces inteiras; portanto, não preciso acessar um banco de dados, mas sim uma implementação zombada que apenas retorna dados estáticos, para que eu possa testar meu método sem se preocupar se o banco de dados está inoperante para manutenção ou não.
fonte
Uma interface é realmente um contrato que as classes de implementação devem seguir; na verdade, é a base para praticamente todos os padrões de design que conheço.
No seu exemplo, a interface é criada porque é garantido que qualquer coisa que IS A Pizza, que significa implementa a interface Pizza, tenha implementado
Após o código mencionado, você pode ter algo parecido com isto:
Dessa forma, você está usando polimorfismo e tudo o que importa é que seus objetos respondam à ordem ().
fonte
Pesquisei a palavra "composição" nesta página e não a vi uma vez. Esta resposta é muito complementar às respostas mencionadas acima.
Uma das razões absolutamente cruciais para o uso de interfaces em um Projeto Orientado a Objetos é que elas permitem que você privilegie a composição sobre a herança. Ao implementar interfaces, você pode separar suas implementações dos vários algoritmos que você está aplicando a eles.
Este excelente tutorial "Decorator Pattern" de Derek Banas (que - engraçado - também usa pizza como exemplo) é uma ilustração que vale a pena:
https://www.youtube.com/watch?v=j40kRwSm4VE
fonte
As interfaces são para aplicar a conexão entre diferentes classes. por exemplo, você tem uma classe para carro e uma árvore;
você deseja adicionar uma funcionalidade gravável para ambas as classes. Mas cada classe tem suas próprias maneiras de queimar. então você simplesmente faz;
fonte
Você obterá interfaces quando precisar delas :) Você pode estudar exemplos, mas precisa do Aha! efeito para realmente obtê-los.
Agora que você sabe o que são interfaces, apenas codifique sem elas. Cedo ou tarde, você terá um problema, em que o uso de interfaces será a coisa mais natural a se fazer.
fonte
Estou surpreso que poucas postagens contenham o motivo mais importante para uma interface: Design Patterns . É a visão geral do uso de contratos e, embora seja uma decoração de sintaxe para o código da máquina (para ser honesto, o compilador provavelmente os ignora), a abstração e as interfaces são essenciais para OOP, entendimento humano e arquiteturas de sistema complexas.
Vamos expandir a analogia da pizza para dizer uma refeição completa de 3 pratos. Ainda teremos a
Prepare()
interface principal para todas as nossas categorias de alimentos, mas também teremos declarações abstratas para as seleções de cursos (entradas, pratos principais, sobremesas) e propriedades diferentes para os tipos de alimentos (salgados / doces, vegetarianos / não vegetarianos, sem glúten etc).Com base nessas especificações, poderíamos implementar o padrão Abstract Factory para conceituar todo o processo, mas usar interfaces para garantir que apenas as fundações fossem concretas. Tudo o resto poderia se tornar flexível ou incentivar o polimorfismo, mas manter o encapsulamento entre as diferentes classes
Course
que implementam aICourse
interface.Se eu tivesse mais tempo, gostaria de elaborar um exemplo completo disso, ou alguém pode estender isso para mim, mas, em resumo, uma interface C # seria a melhor ferramenta para projetar esse tipo de sistema.
fonte
Aqui está uma interface para objetos que têm uma forma retangular:
Tudo o que exige é que você implemente maneiras de acessar a largura e a altura do objeto.
Agora vamos definir um método que funcione em qualquer objeto que seja
IRectangular
:Isso retornará a área de qualquer objeto retangular.
Vamos implementar uma classe
SwimmingPool
retangular:E outra classe
House
que também é retangular:Dado isso, você pode chamar o
Area
método em casas ou piscinas:Dessa maneira, suas classes podem "herdar" o comportamento (métodos estáticos) de qualquer número de interfaces.
fonte
Uma interface define um contrato entre o provedor de uma determinada funcionalidade e os consumidores correspondentes. Separa a implementação do contrato (interface). Você deve dar uma olhada na arquitetura e no design orientados a objetos. Você pode começar com a wikipedia: http://en.wikipedia.org/wiki/Interface_(computing)
fonte
O que ?
As interfaces são basicamente um contrato que todas as classes que implementam a Interface devem seguir. Eles se parecem com uma classe, mas não têm implementação.
Em
C#
Nomes de interface, por convenção, é definido prefixando um 'I'; portanto, se você quiser ter uma interface chamada shapes, a declarará comoIShapes
Agora porque?
Improves code re-usability
Vamos dizer que você quiser chamar
Circle
,Triangle.
é possível agrupá-los e chamá-losShapes
e ter métodos para desenharCircle
eTriangle
Mas ter aplicação concreta seria uma má idéia, porque amanhã você pode decidir ter 2 maisShapes
Rectangle
&Square
. Agora, quando você os adiciona, há uma grande chance de quebrar outras partes do seu código.Com o Interface, você isola a implementação diferente do Contrato
Dia do cenário ao vivo 1
Você foi solicitado a criar um aplicativo para desenhar círculo e triângulo
Cenário ao vivo Dia 2
Se você foi solicitado a adicionar
Square
e adicionarRectangle
, tudo o que você precisa fazer é criar o implentation para eleclass Square: IShapes
eMain
adicionar à listashapes.Add(new Square());
fonte
Há muitas respostas boas aqui, mas eu gostaria de tentar de uma perspectiva ligeiramente diferente.
Você pode estar familiarizado com os princípios do SOLID do design orientado a objetos. Em suma:
S - Princípio da responsabilidade única O - Princípio aberto / fechado L - Liskov Princípio da substituição I - Interface Segregação Princípio D - Dependência Inversão Princípio
Seguir os princípios do SOLID ajuda a produzir um código limpo, bem fatorado, coeso e pouco acoplado. Dado que:
qualquer coisa que ajude no gerenciamento de dependências é uma grande vitória. As interfaces e o Princípio da inversão de dependência realmente ajudam a dissociar o código das dependências de classes concretas, para que o código possa ser escrito e fundamentado em termos de comportamentos, em vez de implementações. Isso ajuda a dividir o código em componentes que podem ser compostos em tempo de execução, em vez de em tempo de compilação, e também significa que esses componentes podem ser facilmente conectados e removidos sem a necessidade de alterar o restante do código.
As interfaces ajudam, em particular, com o Princípio da inversão de dependência, onde o código pode ser componente em uma coleção de serviços, com cada serviço sendo descrito por uma interface. Os serviços podem ser "injetados" nas classes em tempo de execução, passando-os como um parâmetro construtor. Essa técnica realmente se torna crítica se você começar a escrever testes de unidade e usar o desenvolvimento orientado a testes. Tente! Você entenderá rapidamente como as interfaces ajudam a dividir o código em partes gerenciáveis que podem ser testadas individualmente isoladamente.
fonte
O principal objetivo das interfaces é que ele faça um contrato entre você e qualquer outra classe que implemente essa interface, que dissocia o código e permite a expansão.
fonte
Therese pede ótimos exemplos.
Outra, no caso de uma instrução switch, você não precisa mais manter e alternar toda vez que quiser que o rio execute uma tarefa de uma maneira específica.
No seu exemplo de pizza, se você quer fazer uma pizza, a interface é tudo que você precisa; a partir daí, cada pizza cuida de sua própria lógica.
Isso ajuda a reduzir o acoplamento e a complexidade ciclomática. Você ainda precisa implementar a lógica, mas haverá menos para acompanhar na imagem mais ampla.
Para cada pizza, você pode acompanhar as informações específicas dessa pizza. O que outras pizzas têm não importa, porque apenas as outras pizzas precisam saber.
fonte
A maneira mais simples de pensar em interfaces é reconhecer o que significa herança. Se a classe CC herda a classe C, significa que:
Essas duas funções da herança são, em certo sentido, independentes; Embora a herança se aplique aos dois simultaneamente, também é possível aplicar o segundo sem o primeiro. Isso é útil porque permitir que um objeto herde membros de duas ou mais classes não relacionadas é muito mais complicado do que permitir que um tipo de coisa seja substituível por vários tipos.
Uma interface é um pouco como uma classe base abstrata, mas com uma diferença fundamental: um objeto que herda uma classe base não pode herdar nenhuma outra classe. Por outro lado, um objeto pode implementar uma interface sem afetar sua capacidade de herdar qualquer classe desejada ou implementar outras interfaces.
Uma característica interessante disso (subutilizada na estrutura .net, IMHO) é que eles permitem indicar declarativamente as coisas que um objeto pode fazer. Alguns objetos, por exemplo, desejam um objeto de fonte de dados a partir do qual eles podem recuperar itens por índice (como é possível com uma Lista), mas não precisam armazenar nada lá. Outras rotinas precisarão de um objeto de depósito de dados onde possam armazenar coisas não por índice (como no Collection.Add), mas não precisarão ler nada de volta. Alguns tipos de dados permitem acesso por índice, mas não permitem gravação; outros permitirão a escrita, mas não permitirão o acesso pelo índice. Alguns, é claro, permitirão os dois.
Se ReadableByIndex e Appendable fossem classes base não relacionadas, seria impossível definir um tipo que poderia ser passado tanto para as coisas que esperavam um ReadableByIndex quanto para as que esperavam um Appendable. Um poderia tentar mitigar isso, tendo ReadableByIndex ou Appendable derivado do outro; a classe derivada precisaria disponibilizar membros públicos para ambos os fins, mas avise que alguns membros públicos podem não funcionar realmente. Algumas classes e interfaces da Microsoft fazem isso, mas isso é bastante nojento. Uma abordagem mais limpa é ter interfaces para diferentes propósitos e fazer com que os objetos implementem interfaces para as coisas que eles realmente podem fazer. Se alguém tivesse uma interface IReadableByIndex e outra interface IAppendable,
fonte
As interfaces também podem ser encadeadas para criar ainda outra interface. Essa capacidade de implementar várias interfaces oferece ao desenvolvedor a vantagem de adicionar funcionalidade a suas classes sem precisar alterar a funcionalidade de classe atual (Princípios do SOLID)
O = "As classes devem estar abertas para extensão, mas fechadas para modificação"
fonte
Para mim, uma vantagem / benefício de uma interface é que ela é mais flexível que uma classe abstrata. Como você pode herdar apenas 1 classe abstrata, mas implementar várias interfaces, as alterações em um sistema que herda uma classe abstrata em muitos lugares se tornam problemáticas. Se for herdada em 100 locais, uma alteração exigirá alterações em todos os 100. Mas, com a interface, você pode colocar a nova alteração em uma nova interface e apenas usar essa interface onde for necessário (Interface Seq. Do SOLID). Além disso, o uso da memória parece que seria menor com a interface, pois um objeto no exemplo da interface é usado apenas uma vez na memória, apesar de quantos lugares implementam a interface.
fonte
As interfaces são usadas para gerar consistência, de uma maneira que é pouco acoplada, o que a torna diferente da classe abstrata que é fortemente acoplada. É por isso que também é comumente definido como um contrato. definido pela interface e não há elementos concretos nela.
Vou apenas dar um exemplo suportado pelo gráfico abaixo.
Imagine em uma fábrica que existem 3 tipos de máquinas: uma máquina de retângulo, uma máquina de triângulo e uma máquina de polígonos. Os tempos são competitivos e você deseja otimizar o treinamento do operador. Você apenas deseja treiná-los em uma metodologia de partida e parada de máquinas para Agora, em 3 máquinas diferentes, você tem uma maneira consistente de iniciar e parar 3 tipos diferentes de máquinas. Agora imagine que essas máquinas são classes e as classes precisam ter métodos de início e parada, como você vai gerar consistência nessas classes, o que pode ser muito diferente? Interface é a resposta.
Um exemplo simples para ajudá-lo a visualizar, pode-se perguntar por que não usar classe abstrata? Com uma interface, os objetos não precisam ser diretamente relacionados ou herdados e você ainda pode gerar consistência entre diferentes classes.
fonte
Deixe-me descrever isso por uma perspectiva diferente. Vamos criar uma história de acordo com o exemplo que mostrei acima;
Program, Machine e IMachine são os atores de nossa história. O programa deseja executar, mas não possui essa capacidade, e o Machine sabe como executar. Machine e IMachine são melhores amigos, mas o Programa não está falando com o Machine. Então, Program e IMachine fazem um acordo e decidem que o IMachine dirá ao Program como executar olhando Machine (como um refletor).
E o Programa aprende a executar com a ajuda do IMachine.
Interface fornece comunicação e desenvolvimento de projetos fracamente acoplados.
PS: Eu tenho o método da classe concreta como privado. Meu objetivo aqui é obter um acoplamento flexível, impedindo o acesso a propriedades e métodos de classe concretos, e deixando apenas o caminho para alcançá-los por meio de interfaces. (Então eu defini os métodos das interfaces explicitamente).
fonte
Eu sei que estou muito atrasado .. (quase nove anos), mas se alguém quiser uma pequena explicação, você pode fazer isso:
Em palavras simples, você usa a Interface quando sabe o que um Objeto pode fazer ou qual função vamos implementar em um objeto. Exemplo Inserir, Atualizar e Excluir.
Nota importante: interfaces são SEMPRE públicas.
Espero que isto ajude.
fonte