Princípios de Design de Software – Liskov Substitution Principle

Continuando a série de posts sobre os princípios de Design OO, vamos analisar agora um pouco sobre o Princípio da Substituição de Liskov. Aqui também será utilizado como principal referência o texto de Robert C. Martin [1] sobre o princípio.

Liskov Substitution Principle

O LSP é um princípio muito ligado ao OCP, o que ficará mais evidente após o exemplo. Por enquanto vamos ver a definição do princípio.

O LSP sugere que deve ser possível substituir objetos por instâncias de seus subtipos [2]. Pode até parecer óbvio, uma vez que com a herança é possível utilizar subtipos de uma classe, no entanto, vamos analisar um pequeno problema exposto em [1] que exemplifica bem a quebra do princípio. Uma definição um pouco mais exata sobre o princípio pode ser encontrada em [3] e [1]:

Se q(x) é uma propriedade válida para objetos x de um tipo T. Então q(y) deve ser também válida para objetos y de um tipo S, onde S é um subtipo de T.

Analisando de maneira mais prática, no exemplo mostrando em [1] Martin apresenta duas classes: Retângulo e Quadrado. Para o exemplo, o Quadrado é considerado um subtipo de Retângulo, já que o quadrado é um caso especial de retângulo, pois os seus lados são iguais. Mostrando o código seria algo mais ou menos assim:

class Rectangle
{
public:
    virtual void   SetWidth(double w)  {itsWidth=w;}
    virtual void   SetHeight(double h) {itsHeight=w;}
    double GetHeight() const   {return itsHeight;}
    double GetWidth() const    {return itsWidth;}
private:
    double itsWidth;
    double itsHeight;
};

A classe quadrado vai herdar desta classe, mas uma pequena alteração será feita nos métodos set para que ambos os valores sejam alterados ao mesmo tempo. Assim fica garantido que a classe Quadrado está em conformidade com o modelo matemático de um quadrado.

void Square::SetWidth(double w)
{
    Rectangle::SetWidth(w);
    Rectangle::SetHeight(w);
}
void Square::SetHeight(double h)
{
    Rectangle::SetHeight(h);
    Rectangle::SetWidth(h);
}

Veja agora a seguinte asserção:

void g(Rectangle& r)
{
    r.SetWidth(5);
    r.SetHeight(4);
    assert(r.GetWidth() * r.GetHeight()) == 20);
}

Para um retângulo, faz todo o sentido que uma largura de 5 e altura de 4 calcule uma área de 20, no entanto, ao passar um quadrado para este método, a área calculada seria de 4 * 4 = 16, pois o último set alteraria a largura e altura do quadrado para 4. Neste exemplo fica clara a violação ao princípio (Em [1] é possível encontrar uma discussão mais aprofundada sobre este exemplo).

Neste ponto fica então mais clara a ligação com o OCP pois seria necessário prever, de certa forma, que a classe quadrado surgiria e preparar o sistema de uma maneira que ele consiga suportar quadrados e retângulos. Um sistema que obedece a ambos os princípios (OCP e LSP) são considerados realmente reutilizáveis, pois nenhum método causará efeitos colaterais [1].

Um ponto mais profundo a ser discutido sobre o princípio é o Design por Contrato (Design by Contract – DbC) [4], que é uma metáfora sobre elementos de software que devem se comportar como clientes e fornecedores, que utilizam um contrato para garantir obrigações e benefícios. Nesse contexto, clientes oferecem certas garantias, evitando que os métodos chamados sejam obrigados a fazer verificações, e os fornecedores garantem propriedades que beneficiarão o cliente, garantindo assim a conformidade do sistema.

Unindo os conceitos do LSP com o DbC, surge uma ideia apresentada em [1] por Bertrand Meyer: ao redefinir uma rotina (nas classes derivadas), você deve apenas substituir precondições por outras mais fracas, e as pós condições por outras mais fortes. As precondições são condições que precisam ser verdadeiras para que o método seja executado, e ao completar as operações as pós condições precisam ser verdadeiras.

Padrões de Projeto

Um padrão de projeto que claramente exemplifica a substituição de classes mães por classes filhas é o State. O design que o padrão propõe se baseia no fato de que as classes filhas são perfeitas substitutas para suas mães, e esse é um dos problemas ao implementar o padrão, garantir que todas as classes filhas façam transições de estados seguras e válidas.

Além disso, outros padrões também precisam garantir a substituição de interfaces por implementações, como o Strategy, o Template Method ou o Adapter.

Se gostou do post compartilhe com seus amigos e colegas, senão, comente o que pode ser melhorado. Possui alguma outra opinião ou alguma informação adicional? Comenta ai! 😀

Referências:

[1] http://www.objectmentor.com/resources/articles/lsp.pdf

[2] http://en.wikipedia.org/wiki/SOLID_(object-oriented_design)

[3] http://en.wikipedia.org/wiki/Liskov_substitution_principle

[4] http://en.wikipedia.org/wiki/Design_by_contract

Anúncios

3 comentários sobre “Princípios de Design de Software – Liskov Substitution Principle

  1. […] pela primeira letra dos cinco princípios: Single responsibility principle Open/closed principle Liskov substitution principle Interface segregation principle Dependency inversion […]

  2. Cara. Eu adoro seu blog. Ele está salvo nos meus favoritos e uso ele como um guia. Só vou dizer o que vou dizer, porque você deixou aberto para sugestões. No parágrafo abaixo:

    Se q(x) é uma propriedade válida para objetos x de um tipo T. Então q(y) deve ser também válida para objetos y de um tipo S, aonde S é um subtipo de T.

    Mude o ‘aonde’ para ‘onde’. ‘Aonde’ é utilizado quando nos referimos a lugares aos quais alguém vai ou volta, por exemplo: “aonde você foi”, “aonde você vai”, “aonde irei”, etc.

    Mas muito animal seu blog. Sempre recomendo a todos os meus amigos. Um abraço! Parabéns pelo blog.

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s