C# - Violando o Liskov Substitution Principle (LSP)


Vamos recordar um dos princípios SOLID, o princípio da substituição de Liskov ou LSP.

O princípio da substituição de Liskov (LSP) , um dos princípios SOLID, declara que uma classe base deve poder ser substituída por sua classe derivada sem alteração no comportamento final.

Assim, pelo princípio LSP, a instância de uma classe base deve possibilitar sua substituição por instâncias das classes derivadas sem que se necessita qualquer alteração no código.

Esse princípio permite verificar se você esta implementando a herança de forma correta.

Se ocorrer a violação do princípio significa que a herança foi implementada de forma incorreta. Para isso ocorrer basta uma classe derivada sobrescrever um método herdado da classe base e assim mudar o comportamento esperado.

Para perceber isso vamos criar uma classe chamada MinhaCollection com uma propriedade Count pública e uma classe MeuArray que herda de MinhaCollection:

class MinhaCollection
{
     public int Count { get ; set ; }
}
class MeuArray : MinhaCollection
{
}

Podemos então fazer o seguinte:

class Program
{
   static void Main()
   {
        MinhaColecao colecao = new MeuArray();
   }
}

Embora o Count em MeuArray seja imutável visto que arrays possuem um tamanho definido e um número fixo de elementos, isso ainda esta correto.

Até agora tudo ok.

Vamos agora permitir que a classe MinhaColecao adicione novos elementos e a seguir vamos sobrescrever este método na classe MeuArray fazendo outra implementação:

class MinhaCollection
{
     public int Count { get ; set ; }
     public virtual void AddItem(item item)
     {
          Console.WriteLine("Incluir Item");
     }
}
class MeuArray : MinhaCollection
{
     public override void AddItem(item item)
     {
         throw new System.NotSupportedException();     
     }
}

Acabamos de violar o Princípio da Substituição de Liskov.

Embora o código seja compilado,  como podemos converter o MeuArray em MinhaCollection, também podemos adicionar um novo item a um Array de tamanho fixo e isso está incorreto. Mudamos o comportamento.

Então, aqui está um problema que surge ao violar o Princípio de Substituição de Liskov. Nesses casos, temos que lançar uma exceção de tempo de execução.

Na verdade a classe Array da plataforma .NET também viola o LSP visto que ela deriva de ICollection<T>.

Nota:  De acordo com a documentação do MSDN, o System.Array implementa o ICollection, mas o System.Array não fornece uma propriedade Count (mas você sempre pode usar o método de extensão Count()).

class Program
{
    static void Main()
    {
         ICollection<int> collection = new int[] {0};
         collection.Add(1);  // <-- Throws NotSupportedException
    }
} 

O método Add não está visível como membro da matriz, pois é implementado explicitamente. Mas sempre somos capazes de converter uma matriz em ICollection<T> e operar em sua instância a partir do nível da interface subjacente.

E estamos conversados.

"Ninguém jamais viu a Deus; se nos amamos uns aos outros, Deus está em nós, e em nós é perfeito o seu amor.
Nisto conhecemos que estamos nele, e ele em nós, pois que nos deu do seu Espírito.
E vimos, e testificamos que o Pai enviou seu Filho para Salvador do mundo."

1 João 4:12-14

Referências:


José Carlos Macoratti