Interfaces - Qual é o objetivo?

269

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
    }
}
Nebelhom
fonte
Sinto que há duplicatas dessa pergunta aqui no SO, mas todas parecem explicar apenas a parte do contrato de uma interface, então não tenho certeza de que elas se apliquem.
Lasse V. Karlsen
7
tentando ser um usuário agradável e organizado, costumo procurar minha resposta primeiro em vários fóruns antes de publicar algo. Infelizmente, a maioria começou mais tarde e o resto não ajudou. Eu já estava lutando com o básico "Por que fazer isso?" como me pareceu desnecessário complicar demais. Btw. Obrigado a todos pelas respostas muito rápidas. Eu tenho que digerir todos eles primeiro, mas acho que agora tenho uma idéia razoavelmente boa do objetivo deles. Parece que eu sempre olhei para ele de um ângulo diferente. Muito obrigado por sua ajuda.
23911 Nebelhom
1
As interfaces também ajudam a estabelecer herança como para structtipos.
Ja11
3
Hmm, o OP estava perguntando: "Pelo que entendi, a interface é uma forma de contornar a herança múltipla inexistente que não existe em C #. (Fora isso, no exemplo de pizza de livro didático citado) use uma classe base em vez de uma interface ". E, em seguida, a maioria das respostas deu um exemplo que pode ser implementado por uma classe base (abstrata) ou um exemplo para mostrar como a interface é necessária para o cenário de herança múltipla. Essas respostas são todas boas, mas elas não estão apenas reiterando algo que o OP já sabe? Não é à toa que o OP acabou escolhendo uma resposta sem exemplos. LOL
RayLuo

Respostas:

176

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, IPizzavocê pode usá-la IPizzasempre que manipular uma instância de um tipo desconhecido de pizza. Qualquer instância cujo tipo herda IPizzaé garantida como ordenável, pois possui umOrder() método

Python 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, objecto 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).

Joey
fonte
1
+1, embora eu não diria que a interface (no sentido de um contrato) pode ser uma classe abstrata ou normal.
Groo 23/07
3
Eu acrescentaria que você não pode esperar entender interfaces em alguns minutos. Eu acho que não é razoável entender interfaces se você não tem anos de experiência em programação orientada a objetos. Você pode adicionar alguns links aos livros. Eu sugeriria: Injeção de Dependência no .NET, que é realmente a profundidade do buraco rabit, não apenas uma introdução suave.
Knut
2
Ah, há o problema de eu não ter idéia do DI. Mas acho que o principal problema do solicitante foi por que eles são necessários quando, no Python, tudo funciona sem ele. Isso é o que o maior parágrafo da minha resposta tentou fornecer. Não acho que seja necessário aprofundar todos os padrões e práticas que usam interfaces aqui.
Joey
1
Bem, então sua pergunta se torna: "Por que usar linguagens estaticamente tipadas quando linguagens dinamicamente são mais fáceis de programar?". Embora eu não seja o especialista em responder a essa pergunta, posso me aventurar a dizer que o desempenho é a questão decisiva. Quando uma chamada para um objeto python é feita, o processo deve decidir durante o tempo de execução se esse objeto tem um método chamado "Order", enquanto que se você fizer uma chamada para um objeto C #, já está estabelecido que implementa esse método, e o Pode ser feita uma chamada para esse endereço.
Boluc Papuccuoglu
3
@BolucPapuccuoglu: Além disso, com um objeto de tipo estatístico, se alguém souber fooimplementá-lo IWidget, um programador que estiver vendo uma chamada foo.woozle()poderá ver a documentação IWidgete 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 o IWidgetcontrato de interface será implementado de foomaneira consistente com esse contrato. Em uma linguagem dinâmica, por outro lado, não haveria um ponto de referência claro para o que foo.woozle()deveria significar.
supercat
440

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

public class Pizza()
{
    public void Prepare(PizzaType tp)
    {
        switch (tp)
        {
            case PizzaType.StuffedCrust:
                // prepare stuffed crust ingredients in system
                break;

            case PizzaType.DeepDish:
                // prepare deep dish ingredients in system
                break;

            //.... etc.
        }
    }
}

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:

public interface IPizza
{
    void Prepare();
}

public class StuffedCrustPizza : IPizza
{
    public void Prepare()
    {
        // Set settings in system for stuffed crust preparations
    }
}

public class DeepDishPizza : IPizza
{
    public void Prepare()
    {
        // Set settings in system for deep dish preparations
    }
}

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:

public PreparePizzas(IList<IPizza> pizzas)
{
    foreach (IPizza pizza in pizzas)
        pizza.Prepare();
}

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 Preparepreparar automaticamente cada pizza corretamente com base em seu tipo, mesmo que a coleção possua vários tipos de pizzas.

KallDrexx
fonte
11
+1, gostei do exemplo de uso de uma lista para mostrar o uso de uma interface.
Danielunderwood 31/07
21
Boa resposta, mas talvez alterá-lo para esclarecer por que uma interface, neste caso, é melhor do que apenas usando uma classe abstrata (para um exemplo tão simples, uma classe abstrata pode realmente ser melhor?)
Jez
3
Esta é a melhor resposta. Há um pequeno erro de digitação ou você pode simplesmente copiar e colar o código. Na interface C #, você não declara modificadores de acesso como public. Portanto, dentro da interface, deve ser apenasvoid Prepare();
Dush
26
Não vejo como isso responde à pergunta. Esse exemplo poderia ter sido feito com a mesma facilidade com uma classe base e um método abstrato, exatamente o que a pergunta original apontou.
Jamie Kitson
4
As interfaces realmente não estão certas até que você tenha muitas delas por aí. Você só pode herdar de uma única classe, abstrata ou não, mas pode implementar quantas interfaces diferentes em uma única classe desejar. De repente, você não precisa de uma porta na moldura da porta; qualquer coisa que seja IOpenable fará, seja uma porta, uma janela, uma caixa de correio, o nome dele.
31418 mtnielsen
123

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:

// This is our back-end implementation of a troll
class Troll
{
    void Walk(int distance)
    {
        //Implementation here
    }
}

A parte dianteira:

function SpawnCreature()
{
    Troll aTroll = new Troll();

    aTroll.Walk(1);
}

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:

class Orc
{
    void Walk(int distance)
    {
        //Implementation (orcs are faster than trolls)
    }
}

A parte dianteira:

void SpawnCreature(creatureType)
{
    switch(creatureType)
    {
         case Orc:

           Orc anOrc = new Orc();
           anORc.Walk();

          case Troll:

            Troll aTroll = new Troll();
             aTroll.Walk();
    }
}

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:

interface ICreature
{
    void Walk(int distance)
}

public class Troll : ICreature
public class Orc : ICreature 

//etc

Front end é então:

void SpawnCreature(creatureType)
{
    ICreature creature;

    switch(creatureType)
    {
         case Orc:

           creature = new Orc();

          case Troll:

            creature = new Troll();
    }

    creature.Walk();
}

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:

public class CreatureFactory {

 public ICreature GetCreature(creatureType)
 {
    ICreature creature;

    switch(creatureType)
    {
         case Orc:

           creature = new Orc();

          case Troll:

            creature = new Troll();
    }

    return creature;
  }
}

E nosso front end se tornaria:

CreatureFactory _factory;

void SpawnCreature(creatureType)
{
    ICreature creature = _factory.GetCreature(creatureType);

    creature.Walk();
}

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

interface ICanTurnToStone
{
   void TurnToStone();
}

public class Troll: ICreature, ICanTurnToStone

O front end pode então ser:

void SpawnCreatureInSunlight(creatureType)
{
    ICreature creature;

    switch(creatureType)
    {
         case Orc:

           creature = new Orc();

          case Troll:

            creature = new Troll();
    }

    creature.Walk();

    if (creature is ICanTurnToStone)
    {
       (ICanTurnToStone)creature.TurnToStone();
    }
}

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:

public interface ICreatureFactory {
     ICreature GetCreature(string creatureType);
}

Nosso front-end pode então ter isso injetado (por exemplo, um controlador de API MVC) através do construtor (normalmente):

public class CreatureController : Controller {

   private readonly ICreatureFactory _factory;

   public CreatureController(ICreatureFactory factory) {
     _factory = factory;
   }

   public HttpResponseMessage TurnToStone(string creatureType) {

       ICreature creature = _factory.GetCreature(creatureType);

       creature.TurnToStone();

       return Request.CreateResponse(HttpStatusCode.OK);
   }
}

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.

Paddy
fonte
6
Não é irritante quando você encontrar uma resposta que estava tentando dizer o que eram, mas faz isso muito melhor - stackoverflow.com/a/93998/117215
Paddy
18
A boa notícia agora é uma página morta e a sua não é :). Bom exemplo!
Junta '01
1
Sua discussão em C me perdeu um pouco. Mas, eu gosto das suas discussões A e B porque ambas explicam essencialmente como as interfaces podem ser usadas para fornecer tipos comuns de funcionalidade em várias classes. A área de interfaces que ainda é confusa para mim é como as interfaces são usadas para um acoplamento mais flexível. Talvez seja isso que sua discussão em C estava abordando? Se assim for, eu acho que preciso de um exemplo mais detalhado :)
user2075599
1
Parece-me no seu exemplo que o ICreature seria mais adequado para ser uma classe base abstrata (Criatura?) Para os Troll e os Orc. Então, qualquer lógica comum entre criaturas poderia ser implementada lá. Que significa que você não precisa a interface ICreature em tudo ...
Skarsnik
1
@ Skarsnik - é exatamente o que é esta nota: "Um ponto importante a ser observado quando se olha para esse ponto de vista é que você também pode facilmente ter usado uma classe de criatura abstrata e, dessa perspectiva, tem o mesmo efeito."
Paddy
33

Aqui estão seus exemplos reexplicados:

public interface IFood // not Pizza
{
    public void Prepare();

}

public class Pizza : IFood
{
    public void Prepare() // Not order for explanations sake
    {
        //Prepare Pizza
    }
}

public class Burger : IFood
{
    public void Prepare()
    {
        //Prepare Burger
    }
}
Shamim Hafiz
fonte
27

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 ):

public abstract class Food {
    public abstract void Prepare();
}

public class Pizza : Food  {
    public override void Prepare() { /* Prepare pizza */ }
}

public class Burger : Food  {
    public override void Prepare() { /* Prepare Burger */ }
}

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:

public abstract class MenuItem {
    public string Name { get; set; }
    public abstract void BringToTable();
}

// Notice Soda only inherits from MenuItem
public class Soda : MenuItem {
    public override void BringToTable() { /* Bring soda to table */ }
}


// All food needs to be cooked (real food) so we add this
// feature to all food menu items
public interface IFood {
    void Cook();
}

public class Pizza : MenuItem, IFood {
    public override void BringToTable() { /* Bring pizza to table */ }
    public void Cook() { /* Cook Pizza */ }
}

public class Burger : MenuItem, IFood {
    public override void BringToTable() { /* Bring burger to table */ }
    public void Cook() { /* Cook Burger */ }
}

Em seguida, você pode usar todos eles como MenuIteme não se importa com como eles lidam com cada chamada de método.

public class Waiter {
    public void TakeOrder(IEnumerable<MenuItem> order) 
    {
        // Cook first
        // (all except soda because soda is not IFood)
        foreach (var food in order.OfType<IFood>())
            food.Cook();

        // Bring them all to the table
        // (everything, including soda, pizza and burger because they're all menu items)
        foreach (var menuItem in order)
            menuItem.BringToTable();
    }
}
Aleksandar Toplek
fonte
2
Eu tive que rolar até aqui para encontrar uma resposta que realmente responda à pergunta "por que não usar apenas uma classe abstrata em vez de uma interface?". Parece que realmente é apenas para lidar com a falta de herança múltipla do c #.
SyntaxRules
2
Sim, esta é a melhor explicação para mim. O único que explica claramente por que a interface pode ser mais útil do que uma classe abstrata. (ou seja: a pizza é um MenuItem e também é comida enquanto com uma classe abstrata que só pode ser um ou outro, mas não ambos)
Maxter
25

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.

  1. Se for carpinteiro, digo: construa andaimes de madeira.
  2. Se é um encanador, eu digo: "Configure os canos"
  3. Se for um eletricista, digo: "Retire os cabos e substitua-os por cabos de fibra óptica".

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()para BuildScaffold()(ou seja, uma ligeira alteração no nome), também terei que alterar também a classe de chamada (ou seja, a Forepersonclasse) - 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:

If(electrician) then  electrician.FixCablesAndElectricity() 

if(plumber) then plumber.IncreaseWaterPressureAndFixLeaks() 

Eu posso fazer algo assim:

ITradesman tradie = Tradesman.Factory(); // in reality i know it's a plumber, but in the real world you won't know who's on the other side of the tradie assignment.

tradie.Work(); // and then tradie will do the work of a plumber, or electrician etc. depending on what type of tradesman he is. The foreman doesn't need to know anything, apart from telling the anonymous tradie to get to Work()!!

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):

Exemplo 1: sem uma interface

Sem interface (Exemplo 2):

Exemplo 2: sem uma interface

Com uma interface:

Exemplo 3: Os benefícios do uso de 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.

BKSpurgeon
fonte
4
Mais um para o cara que começa a trabalhar com um capuz.
Yusha 10/07/19
11

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.

Vidro quebrado
fonte
3
+1 Isso mesmo, se você digitar duck, não precisará de interfaces. Mas eles reforçam a segurança de tipo mais forte.
Groo
11

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.

bevacqua
fonte
10

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:

foreach (Control ctrl in Controls)
{
    if (ctrl is IMyThemableControl)
        ((IMyThemableControl)ctrl).SetTheme(newTheme);
}

Isso não seria possível sem interfaces, em vez disso, você teria que escrever um código como este:

foreach (Control ctrl in Controls)
{
    if (ctrl is MyThemableButton)
        ((MyThemableButton)ctrl).SetTheme(newTheme);
    else if (ctrl is MyThemableTextBox)
        ((MyThemableTextBox)ctrl).SetTheme(newTheme);
    else if (ctrl is MyThemableGridView)
        ((MyThemableGridView)ctrl).SetTheme(newTheme);
    else ....
}
Lasse V. Karlsen
fonte
Sim, eu sei, você não deve usar "is" e depois lançar, deixe isso de lado.
Lasse V. Karlsen
4
suspiro eu sei, mas isso é incidental aqui.
Lasse V. Karlsen
7

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:

  1. 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.

  2. 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.

Gato Schroedingers
fonte
6

Considere que você não pode usar herança múltipla em C # e, em seguida, analise sua pergunta novamente.

Claus Jørgensen
fonte
5

Interface = contrato, usado para acoplamento solto (consulte GRASP ).

Kirill Polishchuk
fonte
4

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 esperar MyInterface. Portanto, você pode ligar i.drawRectangleou i.drawCubesaber 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, Listvariá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.

James Black
fonte
4

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

public void Order();

Após o código mencionado, você pode ter algo parecido com isto:

public void orderMyPizza(IPizza myPizza) {
//This will always work, because everyone MUST implement order
      myPizza.order();
}

Dessa forma, você está usando polimorfismo e tudo o que importa é que seus objetos respondam à ordem ().

Oscar Gomez
fonte
4

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

Jay Edwards
fonte
2
Estou realmente chocado que esta não seja a melhor resposta. As interfaces em toda a sua utilidade são de maior uso na composição.
Maciej Sitko
3

As interfaces são para aplicar a conexão entre diferentes classes. por exemplo, você tem uma classe para carro e uma árvore;

public class Car { ... }

public class Tree { ... }

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;

public class Car : IBurnable
{
public void Burn() { ... }
}

public class Tree : IBurnable
{
public void Burn() { ... }
}
hakan
fonte
2
A pergunta que me assombra em exemplos como este é: Por que isso ajuda? Posso agora passar o argumento do tipo IBurnable para um método e todas as classes com a interface IBurnable podem ser manipuladas? Igual ao exemplo da Pizza que encontrei. É bom que você possa fazer isso, mas não vejo o benefício, como tal. Você poderia estender o exemplo (porque sou muito grosso no momento) ou dar um exemplo que não funcionaria com ele (novamente, porque me sinto muito grosso no momento). Muito obrigado.
Nebelhom
1
Aceita. Interface = "Pode fazer". Classe / Resumo Calss = "Is A"
Peter.Wang
3

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.

sventevit
fonte
3

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 Courseque implementam a ICourseinterface.

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.

ShaunnyBwoy
fonte
1
Esta resposta merece mais pontos! As interfaces brilham quando usadas em padrões projetados, por exemplo, O Padrão de Estado. Consulte plus.google.com/+ZoranHorvat-Programming para obter informações adicionais
Alex Nolasco
3

Aqui está uma interface para objetos que têm uma forma retangular:

interface IRectangular
{
    Int32 Width();
    Int32 Height();
}

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:

static class Utils
{
    public static Int32 Area(IRectangular rect)
    {
        return rect.Width() * rect.Height();
    }
}

Isso retornará a área de qualquer objeto retangular.

Vamos implementar uma classe SwimmingPoolretangular:

class SwimmingPool : IRectangular
{
    int width;
    int height;

    public SwimmingPool(int w, int h)
    { width = w; height = h; }

    public int Width() { return width; }
    public int Height() { return height; }
}

E outra classe Houseque também é retangular:

class House : IRectangular
{
    int width;
    int height;

    public House(int w, int h)
    { width = w; height = h; }

    public int Width() { return width; }
    public int Height() { return height; }
}

Dado isso, você pode chamar o Areamétodo em casas ou piscinas:

var house = new House(2, 3);

var pool = new SwimmingPool(3, 4);

Console.WriteLine(Utils.Area(house));
Console.WriteLine(Utils.Area(pool));

Dessa maneira, suas classes podem "herdar" o comportamento (métodos estáticos) de qualquer número de interfaces.

dharmatech
fonte
3

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)

casa
fonte
3

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á-los Shapese ter métodos para desenhar Circlee Triangle Mas ter aplicação concreta seria uma má idéia, porque amanhã você pode decidir ter 2 mais Shapes 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

interface IShapes
{
   void DrawShape();
   
 }

class Circle : IShapes
{
    
    public void DrawShape()
    {
        Console.WriteLine("Implementation to Draw a Circle");
    }
}

Class Triangle: IShapes
{
     public void DrawShape()
    {
        Console.WriteLine("Implementation to draw a Triangle");
    }
}
static void Main()
{
     List <IShapes> shapes = new List<IShapes>();
        shapes.Add(new Circle());
        shapes.Add(new Triangle());

        foreach(var shape in shapes)
        {
            shape.DrawShape();
        }
}

Cenário ao vivo Dia 2

Se você foi solicitado a adicionar Squaree adicionar Rectangle, tudo o que você precisa fazer é criar o implentation para ele class Square: IShapese Mainadicionar à listashapes.Add(new Square());

Clint
fonte
Por que você está adicionando uma resposta a uma pergunta de 6 anos com dezenas de outras respostas aprovadas centenas de vezes? Não há nada aqui que ainda não tenha sido dito.
Jonathon Reinhart
@ JonathonReinhart, sim, eu pensei que sim, mas então eu estava pensando neste exemplo e na maneira como ele explica isso ajudará a se relacionar com um corpo melhor do que os outros.
Clint
2

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:

"O gerenciamento de dependências é o principal desafio do software em todas as escalas" (Donald Knuth)

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.

Tim Long
fonte
1

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.

Samir Adel
fonte
1

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.

tam
fonte
1

A maneira mais simples de pensar em interfaces é reconhecer o que significa herança. Se a classe CC herda a classe C, significa que:

  1. A classe CC pode usar qualquer membro público ou protegido da classe C como se fosse seu e, portanto, só precisa implementar coisas que não existem na classe pai.
  2. Uma referência a um CC pode ser passada ou atribuída a uma rotina ou variável que espera uma referência a um C.

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,

supercat
fonte
1

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
1

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.

ScottS
fonte
1

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.

insira a descrição da imagem aqui

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.

public interface IMachine
{
    bool Start();
    bool Stop();
}

public class Car : IMachine
{
    public bool Start()
    {
        Console.WriteLine("Car started");
        return true;
    }

    public bool Stop()
    {
        Console.WriteLine("Car stopped");
        return false;
    }
}

public class Tank : IMachine
{
    public bool Start()
    {
        Console.WriteLine("Tank started");
        return true;
    }

    public bool Stop()
    {
        Console.WriteLine("Tank stopped");
        return false;
    }
}

class Program
{
    static void Main(string[] args)
    {
        var car = new Car();
        car.Start();
        car.Stop();

        var tank = new Tank();
        tank.Start();
        tank.Stop();

    }
}
C.Poh
fonte
1
class Program {
    static void Main(string[] args) {
        IMachine machine = new Machine();
        machine.Run();
        Console.ReadKey();
    }

}

class Machine : IMachine {
    private void Run() {
        Console.WriteLine("Running...");
    }
    void IMachine.Run() => Run();
}

interface IMachine
{
    void Run();
}

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).

Furkan Öztürk
fonte
1

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.

interface ICRUD{
      void InsertData(); // will insert data
      void UpdateData(); // will update data
      void DeleteData(); // will delete data
}

Nota importante: interfaces são SEMPRE públicas.

Espero que isto ajude.

noobprogrammer
fonte