Skip to content

Instantly share code, notes, and snippets.

@Camilotk
Last active September 9, 2019 16:16
Show Gist options
  • Save Camilotk/cc8b1775538aadd97c14f4f5c490d47d to your computer and use it in GitHub Desktop.
Save Camilotk/cc8b1775538aadd97c14f4f5c490d47d to your computer and use it in GitHub Desktop.

LSP - Liskov Substitution Principle

  1. Criar uma classe Pessoa, com propriedade nome e um método que retorna suas propriedades em um Set, nesse caso, apenas nome.
using System;
using System.Collections.Generic;

namespace LSP
{
    public class Pessoa
    {
        public string Nome { get; set; }
        }
    }

    public class Impressao
    {
        public void Imprimir(Pessoa pessoa)
        {
            Console.WriteLine(pessoa.Nome);
        }
    }
}
  1. No Program.cs vamos criar duas instancias de Pessoa e utilizar a Classe Impressao para exibir seus nomes no console.
using System;

namespace LSP
{
    class Program
    {
        static void Main(string[] args)
        {
            var p1 = new Pessoa() { Nome = "Elliot" };
            var p2 = new Pessoa() { Nome = "Mr. Robot" };
            var impressao = new Impressao();
            impressao.Imprimir(p1);
            Console.WriteLine("   ");
            impressao.Imprimir(p2);
            Console.ReadLine();
        }
    }
}
Out:
Elliot

Mr. Robot
  1. Agora imagine que o projeto tem que ser alterado, para que Pessoa seja divido em Física e Jurídica e então decidimos extender essa classe em PessoaFisica e PessoaJuridica
public class PessoaJuridica: Pessoa
    {
        // Herda Nome
        public string RazaoSocial { get; set; }
        public string CNPJ { get; set; }
    }

    public class PessoaFisica : Pessoa
    {
        // Herda Nome
        public string CPF { get; set; }
    }
  1. Agora mudamos a Main para receber uma pessoa Jurídica
using System;

namespace LSP
{
    class Program
    {
        static void Main(string[] args)
        {
            var pj = new PessoaJuridica() { 
                Nome = "Evil Corp.", 
                RazaoSocial = "Evil Corporation", 
                CNPJ = "123"
            };
            var impressao = new Impressao();
            impressao.Imprimir(pj);
            Console.ReadLine();
        }
    }
}
Out:
Evil Corp.

PROBLEMA: Se quisermos exibir a Razão Social- ou outros atributos específicos de uma das classes subtipo - no mesmo método, teremos que alterar o método de impressão. Porém se alterarmos o método para exibir pessoa.RazaoSocial a Classe Pessoa não possui essa propriedade e se alterarmos para receber PessoaJuridica como parâmetro não conseguiremos receber PessoaFisica.

O LSP diz que se recebermos uma Classe Pessoa como parâmetro do método .Imprimir(), o comportamento deverá ser o mesmo para PessoaFisica e para PessoaJuridica. Então temos que garantir que ao imprimirmos as informações de ambas as subclasses de Pessoa o comportamento seja o mesmo.

  1. Para garantir que ao chamarmos o método impressão o comportamento será o mesmo para todas as Classes, iremos criar em ambas classes uma propriedade List que irá guardar todas as informações da mesma - é possivel utilizar o método .ToString() da Classe Object da qual todos os objetos herdam caso seja necessário guardar valores númericos ou de tipos que não são cadeia de caracteres.
using System;
using System.Collections.Generic;

namespace LSP
{
    public class Pessoa
    {
        public string Nome { get; set; }
        // Obs: A keyword virtual significa que ele pode ser rescrito
        public virtual List<string> RetornarDadosPessoa()
        {
            var retorno = new List<string>();
            retorno.Add(Nome);
            return retorno;
        }
    }

    public class PessoaJuridica: Pessoa
    {
        public string RazaoSocial { get; set; }
        public string CNPJ { get; set; }

        public override List<string> RetornarDadosPessoa()
        {
            // O override em C# recebe o comportamento da classe Pai
            // ao instanciala na variavel retorno e depois adiciona os
            // comportamentos abaixo extendendo-a
            var retorno = base.RetornarDadosPessoa();
            retorno.Add(RazaoSocial);
            retorno.Add(CNPJ);
            return retorno;
        }
    }

    public class PessoaFisica : Pessoa
    {
        public string CPF { get; set; }
        public override List<string> RetornarDadosPessoa()
        {
            var retorno = base.RetornarDadosPessoa();
            retorno.Add(CPF);
            return retorno;
        }
    }
}
  1. Alterar o método Impressao para imprimir todos os dados - ou os que queremos.
public class Impressao
{
    public void Imprimir(Pessoa pessoa)
    {
        var dados = pessoa.RetornarDadosPessoa();
        foreach (var s in dados)
        {
            Console.WriteLine(s);
        }
    }
}
  1. Então podemos alterar nossa Main para instanciar uma PessoaFisica e uma PessoaJuridica e imprimir as informações da mesma.
using System;

namespace LSP
{
    class Program
    {
        static void Main(string[] args)
        {
            var pj = new PessoaJuridica() { 
                Nome = "Evil Corp.", 
                RazaoSocial = "Evil Corporation", 
                CNPJ = "123"
            };
            var pf = new PessoaFisica() { 
                Nome = "Elliot", 
                CPF = "123" 
            };
            var impressao = new Impressao();
            impressao.Imprimir(pj);
            Console.WriteLine("   ");
            impressao.Imprimir(pf);
            Console.ReadLine();
        }
    }
}
Out:
Evil Corp
Evil Corporation
123

Elliot
123

Assim podemos atestar que o método .Imprimir() que recebe a Classe Pessoa tem o mesmo comportamento para seus subtipos/subclasses PessoaFisica e PessoaJuridica, cumprindo com o Principio de Substituição de Liskov.

Implementação em Java

Classe Pessoa

import java.util.List;
import java.util.ArrayList;

public class Pessoa {
  protected String _nome;
  protected List<String> _propriedades;

  public Pessoa(String Nome){
    _nome = Nome;
    _propriedades = new ArrayList<String>();
  }

  protected Pessoa(String Nome, List<String> list){
    _nome = Nome;
    _propriedades = list;
  }

  public String getNome() {
    return _nome;
  }

  public void setNome(String value) {
    _nome = value;
  }

  public List<String> RetornarDadosPessoa() {
    _propriedades.add(_nome);
    return _propriedades;
  }
}

Classe Pessoa Juridica

import java.util.List;
import java.util.ArrayList;

 public class PessoaJuridica extends Pessoa {
  private String _razao_social;
  private String _cnpj;

  public PessoaJuridica(String Nome, String Cnpj, String RazaoSocial) {
     super(Nome, new ArrayList<String>());
    _cnpj = Cnpj;
    _razao_social = RazaoSocial;
  }

  public String getRazaoSocial() {
    return _razao_social;
  }

  public void setRazaoSocial(String value) {
    _razao_social = value;
  }

  public String getCnpj() {
    return _cnpj;
  }

  public void setCnpj(String value) {
    _cnpj = value;
  }

  @Override
  public List<String> RetornarDadosPessoa() {
    _propriedades.add(_nome);
    _propriedades.add(_cnpj);
    _propriedades.add(_razao_social);
    return _propriedades;
  }
}

Classe Pessoa Física

import java.util.List;
import java.util.ArrayList;

public class PessoaFisica extends Pessoa {
  private String _cpf;

  PessoaFisica(String Nome, String Cpf) {
    super(Nome, new ArrayList<String>());
    _cpf = Cpf;
  }

  public String getCPF() {
    return _cpf;
  }

  public void setCPF(String value) {
    _cpf = value;
  }
  
  @Override
  public List<String> RetornarDadosPessoa() {
    _propriedades.add(_nome);
    _propriedades.add(_cpf);
    return _propriedades;
  }

}

Classe Impressao

import java.util.*;

public class Impressao {
  public static void Imprimir(Pessoa pessoa) {
      List<String> dados = pessoa.RetornarDadosPessoa();
      for(String dado : dados) {
        System.out.println(dado);
      }
  }
}

Classe Main

class Main {
  public static void main(String[] args) {
    PessoaFisica p1 = new PessoaFisica("Elliot", "123");
    PessoaJuridica p2 = new PessoaJuridica("Evil Corp.", "123", "Evil Corporation");
    Impressao.Imprimir(p1);
    System.out.println("    ");
    Impressao.Imprimir(p2);
  }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment