Veja as opções para criar objetos na linguagem C# e escolha a melhor opção. |
As instruções condicionais, como if-else e switch, são essenciais para qualquer linguagem de programação. Nós usamos essas instruções praticamente todo o dia. No entanto, você sabia que essas instruções não são consideradas uma boa prática no contexto da programação orientada a objetos ?
A melhor forma de mostrar o porquê as instruções condicionais são um code smell ou cheiro de código é começar com um exemplo simples.
Nota: Para acompanhar o artigo você tem que ter noções sobre os princípios SOLID.
Apresentando o problema
Vamos supor que temos uma classe Animal que contém o método GetSons() conforme descrito abaixo:
Aqui temos um bloco de código usando o condicional if/else que realiza várias ações dependendo do tipo de objeto.
Este código viola claramente o Princípio da Responsabilidade Única(SRP) que defende que uma classe deve ter uma única responsabilidade, ou seja um único motivo para ser alterada.
Isso ocorre neste código estamos definindo mais de um comportamento ou responsabilidade como Cachorro, Gato e Papagaio, e, assim temos 3 motivos para alterar esta classe.
Além disso, este código também viola o princípio Aberto/Fechado.(OCP) pois se precisarmos incluir um novo tipo de animal o código acima terá que ser alterado incluindo mais uma condição 'else if', e isso vai forçar a modificar a classe Animal. Toda vez que precisarmos adicionar/estender alguma funcionalidade, devemos alterar seu código-fonte.
Com isso ferimos dois importantes
princípios SOLID.
Como podemos consertar isso ?
Existem várias receitas de refatoração para remover condicionais do seu código como por exemplo:
Usando Polimorfismo
Polimorfismo significa muitas formas, na orientação a objetos você pode enviar uma mesma mensagem para diferentes objetos e fazê-los responder da maneira correta.
Usando polimorfismo podemos :
Existem dois tipos básicos de polimorfismo:
Na maioria dos casos, quando substituímos uma instrução condicional por polimorfismo, lidamos com um subtipo polimorfismo. Esse tipo de polimorfismo na POO significa a capacidade de alterar o comportamento do método fornecendo um método com o mesmo nome em uma classe filha.
Vamos então aplicar essa abordagem que usa o polimorfismo para refatorar o código e assim nos livrar das instruções condicionais. Veja como o código deve ficar :
Veja que agora a classe
Animal foi simplificada, e toda a lógica referente
a cada tipo de Animal foi movida para a sua respectiva classe.
A implementação acima está valorizando o princípio de responsabilidade única
encapsulando o comportamento e a responsabilidade nas classes correspondentes. A
princípio, o comportamento de todos os animais é encapsulado em uma classe,
fazendo com que a classe viole o princípio da responsabilidade única.
Sendo transformadas em subclasses separadas, agora temos as responsabilidades empurradas para elas, então qualquer mudança no comportamento de “Gato” é responsabilidade da classe Gato e não da classe “Animal”, e o mesmo vale para os demais tipos.
A nossa implementação também agora esta aderente ao princípio Aberto/Fechado, vamos ver como. Observe o código a seguir:
public abstract class Animal { public abstract string EmitirSom(); private static String GetSomPolimorfico(Animal animal) { return animal.EmitirSom(); } } |
Note que o método
GetSomPolimorfico() depende da abstração (Animal),
então qualquer classe que implemente a abstração funcionará neste método. Além
disso, se for necessário implementar um novo comportamento como “Peixe”, não
precisaremos fazer nenhuma alteração no método
GetSomPolimorfico() nem na abstração Animal.
Temos apenas que criar uma nova classe “Peixe”, e
isso prova que “Animal” está aberto para mais
extensões e não esta forçando nenhuma modificação.
Esta técnica segue o princípio
Tell-Don't-Ask (diga não peça) que prega
que em vez de perguntar a um objeto sobre seu estado e então realizar ações
baseadas nisso, é muito mais fácil simplesmente dizer ao objeto o que ele
precisa fazer e deixá-lo decidir por si mesmo como fazer isso.
Com isso removemos o código duplicado e nos livramos das instruções
condicionais.
Se você precisar adicionar uma nova variante de execução, tudo o que você
precisa fazer é adicionar uma nova subclasse sem tocar no código existente
(Princípio Aberto/Fechado).
E estamos conversados.
"Multidões que dormem no pó da terra acordarão: uns para a
vida eterna, outros para a vergonha, para o desprezo eterno.
Aqueles que são sábios reluzirão como o brilho do céu, e aqueles que conduzem
muitos à justiça serão como as estrelas, para todo o sempre."
Daniel 12:2,3
Referências:
C# - Lendo e escrevendo em arquivos textos e binários
C# - Entendo o I/O na plataforma .NET
C# - Fluxo assíncrono ou async streams
C#- Apresentando Streams assíncronos