As interfaces devem estender (e ao fazer isso herdar métodos de) outras interfaces

13

Embora essa seja uma pergunta geral, ela também é específica para um problema que estou enfrentando no momento. Atualmente, tenho uma interface especificada na minha solução chamada

public interface IContextProvider
{
   IDataContext { get; set; }
   IAreaContext { get; set; }
}

Essa interface é frequentemente usada em todo o programa e, portanto, tenho fácil acesso aos objetos de que preciso. No entanto, em um nível bastante baixo de uma parte do meu programa, preciso acessar outra classe que use o IAreaContext e realize algumas operações fora dele. Então, eu criei outra interface de fábrica para fazer essa criação chamada:

public interface IEventContextFactory 
{
   IEventContext CreateEventContext(int eventId);
}

Eu tenho uma classe que implementa o IContextProvider e é injetada usando o NinJect. O problema que tenho é que a área em que preciso usar esse IEventContextFactory tem acesso apenas ao IContextProvider e ela mesma usa outra classe que precisará dessa nova interface. Não quero instanciar essa implementação do IEventContextFactory no nível baixo e preferiria trabalhar com a interface IEventContextFactory . No entanto, eu também não quero injetar outro parâmetro através dos construtores apenas para que ele seja passado para a classe que precisa dele, ou seja,

// example of problem
public class MyClass
{
    public MyClass(IContextProvider context, IEventContextFactory event)
    {
       _context = context;
       _event = event;
    }

    public void DoSomething() 
    {
       // the only place _event is used in the class is to pass it through
       var myClass = new MyChildClass(_event);
       myClass.PerformCalculation();      
    }
}

Portanto, minha pergunta principal é: isso seria aceitável ou é mesmo comum ou uma boa prática fazer algo assim (a interface estende outra interface):

public interface IContextProvider : IEventContextFactory

ou devo considerar melhores alternativas para alcançar o que preciso. Se eu não fornecer informações suficientes para dar sugestões, avise-me e posso fornecer mais.

dreza
fonte
2
Eu reformularia o título da sua pergunta como "As interfaces devem herdar interfaces", pois nenhuma interface realmente implementa nada.
Jesse C. Slicer
2
A linguagem usual é que uma interface "estende" seu pai e (ao fazer isso) "herda" os métodos da interface pai, por isso recomendo usar "estender" aqui.
Theodore Murdock
As interfaces podem estender os pais; então, por que seria uma má prática? Se uma interface é legitimamente um superconjunto de outra interface, é claro que deve estendê-la.
Robin Winslow

Respostas:

10

Embora as interfaces descrevam apenas o comportamento sem nenhuma implementação, elas ainda podem participar de hierarquias de herança. Como exemplo, imagine que você tem, digamos, a idéia comum de a Collection. Essa interface forneceria algumas coisas comuns a todas as coleções, como um método para iterar sobre os membros. Então você pode pensar em algumas coleções mais específicas de objetos, como a Listou a Set. Cada um deles herda todos os requisitos de comportamento de a Collection, mas adiciona requisitos adicionais específicos para esse tipo específico de coleção.

Sempre que faz sentido criar uma hierarquia de herança para interfaces, você deve. Se duas interfaces sempre forem encontradas juntas, elas provavelmente deverão ser mescladas.

Zoe
fonte
6

Sim, mas apenas se fizer sentido. Faça as seguintes perguntas e, se uma ou mais forem aplicáveis, é aceitável herdar uma interface de outra.

  1. A interface A estende logicamente a interface B, por exemplo, IList adicionando funcionalidade ao ICollection.
  2. A interface A precisa definir o comportamento já especificado por outra interface, por exemplo, implementando IDisposable se tiver recursos que precisam ser arrumados ou IEnumerable se puder ser iterado.
  3. Toda implementação da interface A requer o comportamento especificado pela interface B?

No seu exemplo, não acho que responda sim a nenhum deles. Talvez seja melhor adicioná-lo como outra propriedade:

public interface IContextProvider
{
   IDataContext DataContext { get; set; }
   IAreaContext AreaContext { get; set; }
   IEventContextFactory EventContextFactory { get; set; }
}
Trevor Pilley
fonte
Como torná-lo uma propriedade seria melhor do que estender a interface, ou é apenas outra maneira de esfolar o gato, por assim dizer?
Dreza 5/09/12
1
A diferença é que, se você IContextProviderestender IEventContextFactory, a ContextProviderimplementação também precisará implementar esse método. Se você adicioná-lo como uma propriedade, estará dizendo que a ContextProviderpossui um, IEventContextFactorymas na verdade não conhece os detalhes da implementação.
Trevor Pilley
4

Sim, é bom estender uma interface de outra.

A questão de saber se é a coisa certa a ser feita em um caso específico depende se existe uma verdadeira relação "é-a" entre os dois.

O que é necessário para um relacionamento "é-a" válido?

Primeiro, deve haver uma diferença real entre as duas interfaces, ou seja, deve haver instâncias que implementam a interface pai, mas não a interface filho. Se não houver e nunca haverá instâncias que não implementem as duas interfaces, as duas interfaces devem ser mescladas.

Segundo, deve ser o caso de que toda instância da interface filho deve necessariamente ser uma instância do pai: não há lógica (razoável) sob a qual você criaria uma instância da interface filho que não faria sentido como uma instância do pai. a interface pai.

Se os dois forem verdadeiros, a herança é o relacionamento correto para as duas interfaces.

Theodore Murdock
fonte
Eu acho que ter classes que usam apenas a interface base e não a derivada não é necessário. Por exemplo, a interface IReadOnlyListpode ser útil, mesmo que todas as minhas classes de lista sejam implementadas IList(das quais é derivada IReadOnlyList).
svick
1

Se a única interface sempre deseja exigir / fornecer a outra, vá em frente. Eu já vi isso em alguns casos com os IDisposiblequais fizeram sentido. Em geral, porém, você deve garantir que pelo menos uma das interfaces se comporte mais como uma característica do que como uma responsabilidade.

Para o seu exemplo, isso não está claro. Se ambos estiverem sempre juntos, faça apenas uma interface. Se um sempre precisar do outro, eu ficaria preocupado com o tipo de herança "X e 1" que muitas vezes leva a múltiplas responsabilidades e / ou outros problemas de abstração com vazamento.

Telastyn
fonte
Obrigado por isso. O que você quer dizer com se comportar mais como uma característica do que como uma responsabilidade?
Dreza
@ Dreza, quero dizer responsabilidade como no SRP no SOLID, e características como algo como características da Scala. Principalmente o conceito de como a interface modela seu problema. Representa uma parte primária do problema ou uma visualização de uma peça comum de tipos díspares?
Telastyn 5/09/12