Olá, boa noite!
Sim, em um projeto real esse tipo de modificação seria inviável. Acredito que a intenção do autor foi demonstrar que o conceito faz uso de múltiplas implementações que evitaria modificar uma classe toda hora, mas talvez não tenha sido o melhor exemplo de aberto e fechado.
Vou colocar um exemplo aqui, talvez elucide um pouco melhor o conceito.
Imagine que você faz um sistema para o RH de uma empresa. E no módulo de pagamentos agora existe a Bonificação.
É simples, todo ano os funcionários receberão uma bonificação no fim de ano e o papel do seu sistema é calcular quanto cada funcionário receberá.
A bonificação consiste em:
Cada funcionário receberá o seu salário + 20%.
Além disso, ganharão um adicional por performance.
Caso o funcionário seja desenvolvedor, receberá 200 reais por entrega.
Caso o funcionário seja gerente, receberá 1000 reais por contratação realizada.
Então você teria algo assim:
public class RhServico
{
public decimal CalcularBonificacao(Funcionario funcionario)
{
decimal bonificacaoBase = funcionario.Salario * 1.2m;
decimal adicional;
if (funcionario.Tipo == TipoFuncionario.Desenvolvedor)
adicional = funcionario.NumeroEntregas * 200;
else
adicional = funcionario.NumeroContratacoes * 1000;
return bonificacaoBase + adicional;
}
}
O problema desse código é simples. Toda vez que um novo tipo de funcionário aparecer eu vou ter que mexer nessa classe. Não apenas nela, mas também no Enum (TipoFuncionario).
Então imagine que agora temos mais um funcionário: Scrum Master. Esse vai receber a bonificação base e o adicional será dado por 500 reais - 5 * numero de sprints atrasadas;
public class RhServico
{
public decimal CalcularBonificacao(Funcionario funcionario)
{
decimal bonificacaoBase = funcionario.Salario * 1.2m;
decimal adicional;
if (funcionario.Tipo == TipoFuncionario.Desenvolvedor)
adicional = funcionario.NumeroEntregas * 200;
else if (funcionario.Tipo == TipoFuncionario.Gerente)
adicional = funcionario.NumeroContratacoes * 1000;
else
adicional = 500 - 5 * funcionario.SprintAtrasadas;
return bonificacaoBase + adicional;
}
}
Constatamos que ela está aberta pra modificação. Toda vez que um cargo for adicionado, tudo precisará ser retestado (espero que tenha teste unitário!).
A questão é: Como isolar um pouco essa modificação para que a lógica compartilhada não seja afetada?
Ao invés de ter uma classe Funcionario apenas, nós podemos deixar ela como abstrata.
Antes:
public class Funcionario
{
public TipoFuncionario Tipo { get; set; }
public string Nome { get; set; }
public int NumeroEntregas { get; set; }
public int NumeroContratacoes { get; set; }
public int SprintAtrasadas { get; set; }
public int Salario { get; set; }
}
Depois:
public abstract class Funcionario
{
public string Nome { get; set; }
public int Salario { get; set; }
public abstract decimal CalcularAdicional();
}
public class Desenvolvedor : Funcionario
{
public int NumeroEntregas { get; set; }
public override decimal CalcularAdicional()
{
return NumeroEntregas * 200;
}
}
public class Gerente : Funcionario
{
public int NumeroContratacoes { get; set; }
public override decimal CalcularAdicional()
{
return NumeroContratacoes * 1000;
}
}
public class ScrumMaster : Funcionario
{
public int SprintAtrasadas { get; set; }
public override decimal CalcularAdicional()
{
return 500 - 5 * SprintAtrasadas;
}
}
E assim, a classe de cálculo agora ficaria assim:
public class RhServico
{
public decimal CalcularBonificacao(Funcionario funcionario)
{
decimal bonificacaoBase = funcionario.Salario * 1.2m;
decimal adicional = funcionario.CalcularAdicional();
return bonificacaoBase + adicional;
}
}
Agora mesmo inserindo novos tipos de funcionários, não precisaria alterar sua lógica compartilhada.
Claro, esse é um exemplo bobo também- que inventei agora, mas acho que ajuda a entender o conceito.