Bem direto. Estou implementando uma interface, mas há uma propriedade desnecessária para essa classe e, de fato, não deve ser usada. Minha idéia inicial era fazer algo como:
int IFoo.Bar
{
get { raise new NotImplementedException(); }
}
Suponho que não há nada errado com isso, por si só, mas não parece "certo". Alguém já se deparou com uma situação semelhante antes? Se sim, como você o abordou?
c#
design
interfaces
implementations
Chris Pratt
fonte
fonte
Respostas:
Este é um exemplo clássico de como as pessoas decidem violar o Princípio da Subtituição de Liskov. Eu o desencorajo fortemente, mas encorajaria possivelmente uma solução diferente:
Se o primeiro for o seu caso, apenas não implemente a interface nessa classe. Pense nisso como uma tomada elétrica onde o furo do aterramento é desnecessário, para que ele não se encaixe no solo. Você não conecta nada com o chão e não é grande coisa! Mas assim que você usa algo que precisa de terreno - você pode ter uma falha espetacular. Melhor não perfurar um buraco falso-terra. Então se sua classe não realmente fazer o que a interface tem a intenção, não implementar a interface.
Aqui estão alguns trechos rápidos da wikipedia:
O Princípio de Substituição de Liskov pode ser simplesmente formulado como: "Não fortaleça as pré-condições e não enfraqueça as pós-condições".
Para interoperabilidade semântica e substituibilidade entre diferentes implementações dos mesmos contratos - é necessário que todos eles se comprometam com os mesmos comportamentos.
O Princípio de Segregação de Interface fala da idéia de que as interfaces devem ser separadas em conjuntos coesos, de modo que você não exija uma interface que faça muitas coisas díspares quando desejar apenas um recurso. Pense novamente na interface de uma tomada elétrica, ela também pode ter um termostato, mas isso dificultaria a instalação de uma tomada elétrica e o uso mais difícil para fins de não aquecimento. Como uma tomada elétrica com termostato, interfaces grandes são difíceis de implementar e difíceis de usar.
fonte
Parece bom para mim, se esta é a sua situação.
No entanto, parece-me que sua interface (ou seu uso) será interrompida se uma classe derivada não implementar realmente tudo isso. Considere dividir essa interface.
Isenção de responsabilidade: isso requer herança múltipla para funcionar corretamente, e não tenho idéia se o C # suporta isso.fonte
Eu me deparei com esta situação. De fato, como apontado em outro lugar, o BCL tem tais instâncias ... tentarei fornecer melhores exemplos e fornecer algumas justificativas:
Quando você já possui uma interface que mantém por motivos de compatibilidade e ...
A interface contém membros obsoletos ou descartados. Por exemplo
BlockingCollection<T>.ICollection.SyncRoot
(entre outros), emboraICollection.SyncRoot
não seja obsoleto, ele será lançadoNotSupportedException
.A interface contém membros documentados para serem opcionais e que a implementação pode gerar a exceção. Por exemplo, no MSDN sobre
IEnumerator.Reset
isso, diz:Por um erro no design da interface, deveria ter sido mais de uma interface em primeiro lugar. É um padrão comum na BCL implementar versões somente leitura de contêineres
NotSupportedException
. Eu já fiz isso, é o que se espera agora ... façoICollection<T>.IsReadOnly
retornotrue
para que você possa dizer a eles. O design correto seria ter uma versão legível da interface e, em seguida, a interface completa herda disso.Não há interface melhor para usar. Por exemplo, eu tenho uma classe que permite acessar itens por índice, verificar se ele contém um item e em qual índice, tem algum tamanho, você pode copiá-lo para uma matriz ... parece um trabalho,
IList<T>
mas minha classe tem um tamanho fixo e não suporta adicionar nem remover, por isso funciona mais como uma matriz do que como uma lista. Mas não háIArray<T>
no BCL .A interface pertence a uma API que é portada para várias plataformas e, na implementação de uma plataforma específica, algumas partes dela não são suportadas. Idealmente, haveria alguma maneira de detectá-lo, para que o código portátil que usa essa API possa decidir o que quer ou não chamar essas partes ... mas se você chamá-las, é totalmente apropriado obtê-las
NotSupportedException
. Isso é particularmente verdade, se for uma porta para uma nova plataforma que não estava prevista no design original.Considere também por que não é suportado?
Às vezes
InvalidOperationException
é uma opção melhor. Por exemplo, mais uma maneira de adicionar polimorfismo em uma classe é ter várias implementações de uma interface interna e seu código está escolhendo qual instanciar, dependendo dos parâmetros fornecidos no construtor da classe. [Isso é particularmente útil se você souber que o conjunto de opções é fixo e não deseja permitir que classes de terceiros sejam introduzidas por injeção de dependência.] Fiz isso para suportar o ThreadLocal porque a implementação de rastreamento e não rastreamento é distante demais, e o queThreadLocal.Values
lança na implementação sem rastreamento?InvalidOperationException
mesmo assim, não depende do estado do objeto. Nesse caso, eu mesmo apresentei a classe e sabia que esse método tinha que ser implementado apenas lançando uma exceção.Às vezes, um valor padrão faz sentido. Por exemplo, no
ICollection<T>.IsReadOnly
mencionado acima, faz sentido retornar apenas "verdadeiro" ou "falso", dependendo do caso. Então ... qual é a semânticaIFoo.Bar
? talvez haja algum valor padrão sensato para retornar.Adendo: se você está no controle da interface (e não precisa ficar com ela por compatibilidade), não deve haver um caso em que você precise jogar
NotSupportedException
. No entanto, pode ser necessário dividir a interface em duas ou mais interfaces menores para se adequar ao seu caso, o que pode levar à "poluição" em situações extremas.fonte
Sim, os designers da biblioteca .Net fizeram. A classe Dictionary faz o que você está fazendo. Ele usa implementação explícita para ocultar efetivamente * alguns dos métodos IDictionary. É explicado melhor aqui , mas, para resumir, para usar os métodos Add, CopyTo ou Remove do Dictionary que levam KeyValuePairs, você deve primeiro converter o objeto em um IDictionary.
* Não está "ocultando" os métodos no sentido estrito da palavra "ocultar", como a Microsoft usa . Mas não conheço um termo melhor nesse caso.
fonte
Você sempre pode implementar e retornar um valor benigno ou valor padrão. Afinal, é apenas uma propriedade. Pode retornar 0 (a propriedade padrão de int) ou qualquer valor que faça sentido na sua implementação (int.MinValue, int.MaxValue, 1, 42, etc ...)
Lançar uma exceção parece uma má forma.
fonte