É uma violação do Princípio Aberto-Fechado atualizar uma constante que representa um valor do mundo real?

10

Eu tenho uma classe que calcula a renda anual líquida dos trabalhadores. Tem uma constante que representa uma porcentagem de imposto. Mas um dia a taxa de imposto mudou, então preciso corrigir o código.

O ato de fixar essa constante indica uma violação do Princípio Aberto-Fechado , uma vez que postula que uma classe deve ser fechada para modificação?

Paradisys de Análise
fonte
10
O software muda porque o mundo real muda. Por outro lado, transformar uma porcentagem de imposto em uma constante não é tanto uma violação do Princípio Aberto-Fechado, mas é apenas uma coisa ignorante a ser feita. A porcentagem de imposto é um item alterável óbvio que deve ser vinculado no tempo de execução.
Richard Chambers
4
Eu concordo completamente com Richard. Se você precisar alterar o código para corrigir essa "constante", o OCP é o menor dos seus problemas.
Robert Harvey
3
O que constitui uma violação do OCP é altamente subjetivo e a coisa toda é um pouco obsoleta de qualquer maneira (já que a herança de implementação não é mais uma prática recomendada). Essa é uma pergunta típica em que você precisa adivinhar o que a pessoa que está fazendo a pergunta pensa.
Robert Bräutigam
2
@ DocBrown: o que constitui um "novo requisito"? Você me mostra algum código, posso apontar novos requisitos que definitivamente exigirão alteração de código, independentemente de como o OCP esteja em conformidade com você. Voltemos à pergunta: se o desenvolvedor perguntou ao especialista em negócios sobre isso e não havia expectativa de que a taxa de imposto fosse alterada mais de uma vez a cada dois anos, não há sentido em torná-la configurável ou injetável. Basta mantê-lo simples e se preparar para o que você sabe . E, para essas coisas, claro, torne-o externo à classe. Então depende .
Robert Bräutigam
11
@ RobertBräutigam: o que quero dizer é que não existe IMHO como "conformidade com OCP", existe apenas "conformidade com OCP no contexto de determinadas categorias de requisitos". Certamente pode haver alguma subjetividade para quais categorias um componente deve estar "em conformidade com o OCP". Porém, no caso descrito nesta pergunta, da maneira que eu a entendo, um requisito de mudança já foi identificado, de modo que essa "classe de cálculo de renda" claramente não obedece ao OCP no contexto desse requisito específico.
Doc Brown

Respostas:

14

O OCP pode ser melhor compreendido quando se pensa em classes ou componentes fornecidos por um fornecedor A em algum tipo de caixa preta, para uso dos usuários B, C e D (observe que este é apenas um modelo mental que estou usando para maior clareza, não importa se, na realidade, o único usuário da classe é o próprio A).

Se B, C e D puderem usar ou reutilizar as classes fornecidas para diferentes casos de uso, sem a necessidade de modificação do código-fonte da biblioteca, o componente atenderá ao OCP ( em relação a uma categoria de casos de uso ). Existem diferentes meios para conseguir isso, como

  • tornando a classe herdável (geralmente em conjunto com o padrão de método de modelo ou o padrão de estratégia)

  • fornecendo "pontos de injeção" para injeção de dependência

  • fornecendo parâmetros de configuração para a classe ou componente (por exemplo, tendo um parâmetro do construtor "porcentagem de imposto", como no seu caso, ou usando outro mecanismo de configuração)

  • talvez outros meios, dependendo da linguagem de programação ou ecossistema

Os exemplos típicos que você encontra nos livros didáticos geralmente são do primeiro ou do segundo tipo (acho que, aos olhos dos autores desses livros, o terceiro tipo é trivial demais para ser mencionado).

Como você vê, isso não tem nada a ver com proibir qualquer alteração do código-fonte pelo fornecedor A (como correção de erros, otimização ou adição de novos recursos de uma maneira compatível com versões anteriores), que não tem nenhuma relação com o OCP. O OCP é sobre como A projeta a interface e a granularidade dos componentes na lib, para que diferentes cenários de reutilização (como resuage com diferentes taxas de imposto) não induzam automaticamente requisitos de mudança.

Portanto, apesar do que os outros lhe disseram aqui, a resposta é claramente "sim" , seria uma violação do OCP.

EDIT: parece que entre alguém escreveu um post detalhado sobre exatamente esse tópico. Embora algumas partes possam ter sido melhor formuladas (como Derek Elkins apontou), parece que o autor geralmente compartilha do meu ponto de vista que "cumprir o OCP" não é uma propriedade absoluta, mas algo que só pode ser avaliado no contexto de certas categorias de alterações de requisitos.

Doc Brown
fonte
OK, então o OCP trata de fornecer um comportamento extensível para diferentes casos de uso com um dos três meios que você listou, certo? Mas e se o exemplo de um OP implicasse que algo fundamental está prestes a mudar? Não sei de que OP é o país, mas, no meu país, a taxa de imposto é algo que não muda com muita frequência. Foi um exemplo ruim, mas talvez tenha sido extraído intencionalmente de forma constante, para enfatizar o ponto. Tenho certeza de que não era para ser configurável ou extensível. Portanto, talvez a pergunta fosse sobre "isso não tem nada a ver com proibir qualquer alteração do código-fonte pelo fornecedor A".
Vadim Samokhin
Pelo menos eu entendi dessa maneira. O pobre rapaz que excluiu sua resposta aceita o fez, ou eu acho. Você viu isso de um ângulo um pouco diferente - entendo seu ponto de vista e concordo com isso. Mas parece que o comentário mais sábio foi dado por @Robert Bräutigam. Até agora não percebi que o OCP é tão subjetivo.
Vadim Samokhin
11
Acho que, se alguma vez me perguntam a mesma pergunta, há uma pergunta que devo responder: "esse comportamento deveria ser estendido ou configurado de alguma forma?". Se sim - a modificação direta de uma classe em si é uma violação do OCP. Se não - que OCP simplesmente não é aplicável nessa situação.
Vadim Samokhin
11
@ Zapadlo: Eu acho que se um componente preenche o OCP para uma classe de requisitos não é muito subjetivo - é bastante claro na maioria dos casos se um novo requisito precisa de uma modificação do código fonte de um componente ou se o componente suporta esse requisito ". As possíveis abordagens para implementá-lo não se restringem aos três primeiros meios que listei, veja minha edição. Sua noção de subjetividade pode ser causada porque o OCP tem apenas um nome enganador e é muito mal explicado em muitos livros didáticos.
Doc Brown
Minha noção de subjetividade foi causada pelo fato de eu não entender completamente o que você disse - mas entendo agora, acho. Muito obrigado por comentários perspicazes e sua resposta.
Vadim Samokhin
4

Como outros dizem, idealmente, a classe de renda do trabalhador permitiria a parametrização da constante, tornando essa classe independente desse valor.

Por fim, o aplicativo de chamada também pode permitir a parametrização em termos de configuração externa (por exemplo, um arquivo). Assim que tivermos uma configuração externa, podemos alterar a taxa de imposto - embora considere que, se o arquivo de configuração for lido apenas uma vez na inicialização, o aplicativo precisará ser reiniciado para que as porcentagens de impostos atualizadas entrem em vigor, portanto é algo a manter mente. Poderíamos fornecer um recurso de aplicativo para reler a configuração quando solicitado, ou fornecer um mecanismo mais complicado que notará quando o arquivo de configuração é alterado ...

A longo prazo, você pode achar que as questões tributárias exigem mais do que apenas uma porcentagem - por exemplo, que um dia as leis tributárias são mais complexas e exigem várias porcentagens e algumas constantes (por exemplo, o valor abaixo de US $ 10 mil tributado em X%, enquanto o restante tributado em Y%).

Isso basicamente sugere o uso de um padrão de estratégia, em que a classe principal em questão aqui aceita um objeto de estratégia para calcular o imposto.

As várias estratégias (e% 's e $ constantes) devem ser escolhidas no arquivo de configuração e, agora, adicionar uma nova estratégia exige a adição de um novo código, mas não necessariamente a atualização do código existente.

Cada estratégia pode saber como analisar / interpretar seus próprios argumentos de configuração externa, além de como calcular o imposto real.

Dinamicamente, o imposto pode depender ainda mais do código de idioma do governo, portanto, você pode ter um código de idioma associado a ganhos ou a funcionários (ou ambos). Na configuração externa, podemos associar localidade à estratégia tributária.


Veja também injeção de dependência , onde gerenciamos essas coisas explicitamente.

Erik Eidt
fonte
11
A questão não era se é uma má idéia enterrar algo como uma porcentagem de imposto no código, tenho certeza de que isso é óbvio para a maioria de nós aqui (incluindo o OP). A pergunta era: "isso viola o OCP?" Portanto, não vejo como sua resposta se refere a essa pergunta.
Doc Brown
1

Se você precisar modificar a classe para alterar o valor do imposto, seu design estará violando o OCP. O design apropriado, para o que você descreveu até agora, é que a classe da calculadora tome o valor do imposto como parâmetro.

Se sua classe for instanciada (o que significa que não é uma classe estática), tornando a propriedade da classe variável de imposto, cujo valor é injetado pelo construtor, você também estará melhorando a coesão da classe.

Em resumo, seu design atual faz com que sua classe dependa de um valor constante que não seja realmente constante (definindo constante como um valor que nunca mudaria, independentemente do que fosse, como o valor do PI). Viola o OCP. Altere o design para receber o valor do imposto como argumento do construtor.

Christopher Francisco
fonte
0

Concordo totalmente com o @Becuzz, e só quero resumir: OCP é sobre encontrar abstrações reutilizadas (portanto úteis) que são injetadas em uma classe. Portanto, o comportamento da classe é modificado não alterando seu código, mas fornecendo diferentes implementações. Isso fica claro no livro de Robert Martin " Desenvolvimento, princípios, padrões e práticas de software ágil ", verifique o capítulo correspondente "O princípio de abertura e fechamento", subcapítulo "Abstração é a chave". Esclarece outro equívoco de que o comportamento pode ser modificado apenas com herança. Foi Bertrand Meyer quem propôs isso em 1988 em seu livro " Construção de software orientada a objetos ", não Robert Martin.

Vadim Samokhin
fonte
-2

A meu ver, não é uma violação do princípio aberto e fechado. No entanto, o fato de que algo que pode mudar no tempo (como a porcentagem de imposto) é uma constante é uma falha de design: você não deve alterar o valor da constante, mas como lida com a porcentagem de imposto. Deve ser algum tipo de configuração que pode ser modificada sem recompilar tudo.

Zalomon
fonte
A "falha de design" é que ele está violando o princípio de aberto e fechado, pois você precisa recompilar o código para alterar a constante?
Erdrik Ironrose