O que é "Soft Coding", realmente?

87

Em este artigo por Alex Papadimoulis, você pode ver este trecho:

private void attachSupplementalDocuments()
{
  if (stateCode == "AZ" || stateCode == "TX") {

    //SR008-04X/I are always required in these states
    attachDocument("SR008-04X");
    attachDocument("SR008-04XI");
  }

  if (ledgerAmnt >= 500000) {
    //Ledger of 500K or more requires AUTHLDG-1A
    attachDocument("AUTHLDG-1A");
  }

  if (coInsuredCount >= 5  && orgStatusCode != "CORP") {
    //Non-CORP orgs with 5 or more co-ins require AUTHCNS-1A
    attachDocument("AUTHCNS-1A");
  }
}

Eu realmente não entendo este artigo.

Eu cito:

Se cada constante de regras de negócios foi armazenado em algum arquivo de configuração, a vida seria muito [mais ( sic )] difícil para todos manter o software: haveria um monte de arquivos de código que compartilhou um, grande arquivo (ou, o contrário, um monte de pequenos arquivos de configuração); a implantação de alterações nas regras de negócios não exige um novo código, mas a alteração manual dos arquivos de configuração; e depuração é muito mais difícil.

Este é um argumento contra o número inteiro constante "500000" em um arquivo de configuração ou o "AUTHCNS-1A" e outras constantes de seqüência de caracteres.

Como isso pode ser uma má prática?

Nesse snippet, "500000" não é um número. Não é, por exemplo, o mesmo que:

int doubleMe(int a) { return a * 2;}

onde 2, é um número que não precisa ser abstraído. Seu uso é óbvio e não representa algo que possa ser reutilizado posteriormente.

Pelo contrário, "500000" não é simplesmente um número. É um valor significativo, que representa a ideia de um ponto de interrupção na funcionalidade. Esse número pode ser usado em mais de um local, mas não é o número que você está usando; é a ideia do limite / limite, abaixo do qual uma regra se aplica e acima do qual outra regra.

Como está se referindo a ele a partir de um arquivo de configuração, ou mesmo a #define, constou qualquer que seja o idioma fornecido, pior do que incluir seu valor? Se mais tarde no programa, ou algum outro programador, também exigir esse limite, para que o software faça outra escolha, você estará ferrado (porque quando ele muda, nada garante que ele será alterado nos dois arquivos). Isso é claramente pior para a depuração.

Além disso, se amanhã, o governo exigir "De 5/3/2050, você precisará adicionar AUTHLDG-122B em vez de AUTHLDG-1A", essa constante de sequência não será uma constante de sequência simples. É aquele que representa uma ideia; é apenas o valor atual dessa ideia (que é "a coisa que você adiciona se o razão estiver acima de 500k").

Deixe-me esclarecer. Não estou dizendo que o artigo está errado; Eu simplesmente não entendo; talvez não esteja muito bem explicado (pelo menos para o meu pensamento).

Entendo que a substituição de cada valor literal ou numérico de string possível por uma variável constante, definida ou de configuração não é apenas não necessária, mas complicada demais, mas esse exemplo em particular não parece se encaixar nessa categoria. Como você sabe que não precisará mais tarde? Ou alguém mais para esse assunto?

K. Gkinis
fonte
21
Jogue o quebra-cabeça: qual seria um bom nome para esses números? Acho que você descobrirá que o nome não agrega valor algum, ou descreve tudo o que o código já está descrevendo e, muitas vezes, adiciona ambiguidade ("LedgerLimitForAuthDlg1A"?). Achei o artigo brilhante exatamente por causa da relevância disso. Eu mantive sistemas que usaram as duas abordagens e posso dizer que as regras de negócios pertencem ao código - as torna muito mais fáceis de rastrear, manter e entender. Quando você usa a configuração, é melhor fazer valer a pena - é muito mais caro.
Luaan
2
A configuração deve ser reservada para itens que precisam ser configurados. Se as regras de negócios não são configuráveis ​​em geral, colocar alguns bits na configuração não ajuda em nada.
biziclop
Para idiomas adequadamente avançados, a configuração assume a forma de sub-rotinas reais e não de seqüências de caracteres.
Thorbjørn Ravn Andersen

Respostas:

100

O autor está alertando contra a abstração prematura.

A linha se if (ledgerAmt > 500000)parece com o tipo de regra de negócios que você esperaria ver em grandes sistemas de negócios complexos, cujos requisitos são incrivelmente complexos, mas precisos e bem documentados.

Normalmente, esses tipos de requisitos são casos excepcionais / de ponta, em vez de lógica útil reutilizável. Esses requisitos geralmente são de propriedade e mantidos por analistas de negócios e especialistas no assunto, e não por engenheiros

(Observe que a 'propriedade' de requisitos por analistas de negócios / especialistas nesses casos geralmente ocorre em que desenvolvedores que trabalham em áreas especializadas não têm experiência suficiente em domínio; embora eu ainda esperasse uma comunicação / cooperação completa entre desenvolvedores e especialistas em domínio para proteger contra requisitos ambíguos ou mal escritos.)

Ao manter sistemas cujos requisitos estão repletos de casos extremos e lógica altamente complexa, geralmente não há como abstrair utilmente essa lógica ou torná-la mais sustentável; tentativas de criar abstrações podem facilmente sair pela culatra - não apenas resultando em perda de tempo, mas também em código menos sustentável.

Como está se referindo a ele a partir de um arquivo de configuração, ou mesmo de um #define, const ou o que a sua linguagem fornecer, pior do que incluir seu valor? Se mais tarde no programa, ou algum outro programador, também exigir esse limite, para que o software faça outra escolha, você estará ferrado (porque quando ele muda, nada garante que ele será alterado nos dois arquivos). Isso é claramente pior para a depuração.

Esse tipo de código tende a ser protegido pelo fato de que o próprio código provavelmente possui um mapeamento individual para os requisitos; ou seja, quando um desenvolvedor sabe que a 500000figura aparece duas vezes nos requisitos, ele também sabe que aparece duas vezes no código.

Considere o outro cenário (igualmente provável) em que 500000aparece em vários locais no documento de requisitos, mas os especialistas no assunto decidem alterar apenas um deles; lá você tem um risco ainda maior de que alguém que altere o constvalor não perceba que 500000é usado para significar coisas diferentes - então o desenvolvedor o altera no único local em que ele encontra no código e acaba quebrando algo que não sabia que eles haviam mudado.

Esse cenário ocorre muito no software jurídico / financeiro sob medida (por exemplo, lógica de cotação de seguro) - as pessoas que escrevem esses documentos não são engenheiros e não têm problema em copiar + colar partes inteiras da especificação, modificando algumas palavras / números, mas deixando a maior parte do mesmo.

Nesses cenários, a melhor maneira de lidar com os requisitos de copiar e colar é escrever o código de copiar e colar e fazer com que o código pareça o mais semelhante possível aos requisitos (incluindo codificar todos os dados).

A realidade de tais requisitos é que eles geralmente não ficam copiados e colados por muito tempo, e os valores às vezes mudam regularmente, mas geralmente não mudam em conjunto, portanto, tentando racionalizar ou abstrair esses requisitos ou simplificar eles acabam criando mais dor de cabeça para manutenção do que apenas traduzir requisitos literalmente em código.

Ben Cottrell
fonte
28
Uma linguagem específica de domínio (DSL) pode ser uma boa maneira de tornar o código mais parecido com o documento de requisitos.
Ian em
13
Outra vantagem de uma DSL é que também dificulta a combinação acidental de lógica de aplicativo, apresentação ou persistência com as regras de negócios.
precisa saber é o seguinte
16
Pensar que seu aplicativo é especial o suficiente para garantir seu próprio DSL geralmente é arrogância.
brian_o
8
Those requirements are typically owned and maintained by business analysts and subject matter experts, rather than by engineerso que nem sempre é uma boa ideia. Às vezes, o ato de transformar esses requisitos em código revelará casos extremos onde os requisitos não são bem definidos ou são definidos de maneira a contrariar o interesse comercial. Se os analistas de negócios e desenvolvedores puderem cooperar para alcançar um objetivo comum, muitos problemas poderão ser evitados.
kasperd
4
@ BenCottrell Eu não estava sugerindo alterar as regras para facilitar a gravação do software. Mas quando você tem muitos condicionais nas regras, é perfeitamente possível que alguma interação entre eles tenha sido perdida ao definir as regras em primeiro lugar. Mas, à medida que você transforma a especificação em código, o desenvolvedor deve perceber que há uma possível interação entre essas condições. Nesse ponto, é possível que o desenvolvedor descubra que uma interpretação estrita da especificação leva a um preço não intencional que permitiria que os clientes jogassem o sistema.
kasperd
44

O artigo tem um bom argumento. Como pode ser uma má prática extrair constantes para um arquivo de configuração? Pode ser uma prática ruim se complicar o código desnecessariamente. Ter um valor diretamente no código é muito mais simples do que ter que lê-lo em um arquivo de configuração, e o código, como está escrito, é fácil de seguir.

Além disso, amanhã, o governo diz "De 3/20/2050, você precisa adicionar AUTHLDG-122B em vez de AUTHLDG-1A".

Sim, então você muda o código. O objetivo do artigo é que não é mais complicado alterar o código do que alterar um arquivo de configuração.

A abordagem descrita no artigo não é escalável se você obtiver uma lógica mais complexa, mas o ponto é que você precisa fazer um julgamento e, às vezes, a solução mais simples simplesmente é a melhor.

Como você sabe que não precisará mais tarde? Ou alguém mais para esse assunto?

Este é o ponto do princípio YAGNI. Não projete para um futuro desconhecido, que pode ser completamente diferente, projete para o presente. Você está certo de que, se o valor 500000 for usado em vários locais do programa, é claro que ele deve ser extraído para uma constante. Mas esse não é o caso no código em questão.

Softcoding é realmente uma questão de separação de preocupações . Você codifica as informações que você sabe que podem mudar independentemente da lógica do aplicativo principal. Você nunca codificaria uma string de conexão com um banco de dados, porque sabe que ela pode mudar independentemente da lógica do aplicativo e precisará diferenciá-la para diferentes ambientes. Em um aplicativo da web, gostamos de separar a lógica de negócios dos modelos html e das folhas de estilos, porque eles podem mudar de forma independente e até serem alterados por pessoas diferentes.

Porém, no caso do exemplo de código, as seqüências codificadas e os números codificados são parte integrante da lógica do aplicativo. É concebível que um arquivo possa mudar seu nome devido a alguma alteração de política fora do seu controle, mas é igualmente concebível que precisamos adicionar uma nova verificação if-branch para uma condição diferente. A extração dos nomes e números dos arquivos realmente quebra a coesão nesse caso.

JacquesB
fonte
4
Muitas vezes, é muito mais complicado alterar o código do que um arquivo de configuração. Você pode precisar de um desenvolvedor e de um sistema de compilação / ciclo de lançamento para o primeiro, enquanto o último exige apenas a alteração de um número em uma caixa em uma interface de configuração amigável.
precisa saber é o seguinte
6
@ OrangeDog Sim, é assim que parece à primeira vista. Mas se você faz coisas como esta, a interface de configuração vai ser nada , mas amigável, com centenas de caixas de texto totalmente sem sentido pedir-lhe para quem sabe o que. E agora você precisa criar a interface do usuário e documentá-la. Lembre-se, isso não significa que a configuração nunca seja um bom caminho - há casos em que é absolutamente a escolha certa. Mas não em nenhum dos exemplos deste artigo. E quando foi a última vez que uma legislação mudou apenas o número? A última vez que as regras do IVA foram alteradas aqui, tivemos que refazer todos os cálculos de qualquer maneira.
Luaan 8/04/16
2
@ OrangeDog: Você está assumindo, aqui, que a configuração do software fornece os ganchos necessários para a verificação que você precisa fazer. Observe como no OP todos e cada um ifé baseado em uma variável diferente! Se a variável necessária não estiver acessível na configuração, você precisará modificar o software de qualquer maneira.
Matthieu M.
2
@OrangeDog, então você está sugerindo que haja mudanças significativas na lógica de um aplicativo de software, sem um ciclo de desenvolvimento / versão / versão e testes apropriados?
NPSF3000 08/04
3
@OrangeDog: OK, você usa o YAML para configurar a lógica no exemplo. Como a lógica inclui regras condicionais, você encontra uma maneira de representá-las no YAML. Parabéns, você reinventou o Python. Por que não escrever todo o aplicativo em Python então?
precisa saber é o seguinte
26

O artigo continua falando sobre o 'Enterprise Rule Engine', que provavelmente é um exemplo melhor do que ele está argumentando.

A lógica é que você pode generalizar até o ponto em que sua configuração se torna tão complicada que contém sua própria linguagem de programação.

Por exemplo, o código de estado para mapear o documento no exemplo pode ser movido para um arquivo de configuração. Mas você precisaria expressar um relacionamento complexo.

<statecode id="AZ">
    <document id="SR008-04X"/>
    <document id="SR008-04XI"/>
</statecode>

Talvez você também coloque o valor do razão?

<statecode id="ALL">
    <document id="AUTHLDG-1A" rule="ledgerAmt >= 50000"/>
</statecode>

Em breve, você descobrirá que está programando em um novo idioma que você inventou e salvando esse código em arquivos de configuração que não têm controle de origem ou alteração.

Note-se que este artigo é de 2007, quando esse tipo de coisa era uma abordagem comum.

Atualmente, provavelmente resolveríamos o problema com injeção de dependência (DI). Ou seja, você teria um 'codificado'

InvoiceRules_America2007 : InvoiceRules

que você substituiria por um código rígido ou mais configurável

InvoiceRules_America2008 : InvoiceRules

quando os requisitos legais ou comerciais mudaram.

Ewan
fonte
4
Talvez você deva definir "DI". E talvez explique um pouco mais.
Basil Bourque 08/04
9
Por que esse arquivo não estaria no sistema de controle de origem?
JDługosz 08/04
2
Se for específico do cliente, a versão codificada possui uma enorme bagunça de ifinstruções para fornecer valores diferentes para cada cliente? Parece algo que deve estar em um arquivo de configuração. Estar em um tipo de arquivo ou outro, sendo o resto igual, não é um motivo para não controlar / rastrear / fazer backup do arquivo. O @ewan parece estar dizendo que um arquivo de DSL não pode ser salvo como parte do projeto por algum motivo, quando até mesmo ativos que não sejam de código, como imagens, arquivos de som e documentação, certamente o são .
JDługosz 09/04
2
Você realmente deve refatorar o valor "50000" do seu XML e colocá-lo em um arquivo de configuração separado, não acha? ... e deveria ser 500000, por sinal.
Curinga
1
@jdlugosz o conceito de um ERE é que você compra o sistema e o configura para suas necessidades. talvez porque os desenvolvedores internos estivessem em concorrência com esses sistemas "flexíveis", eles tentariam imitá-los. alterar o controle da configuração, mesmo em sistemas de grandes empresas como a IBM, muitas vezes uma reflexão tardia. O ponto de venda foi a mudança rápida
Ewan
17

Pelo contrário, "500000" não é simplesmente um número. É um valor significativo, que representa a ideia de um ponto de interrupção na funcionalidade. Esse número pode ser usado em mais de um local, mas não é o número que você está usando, é a ideia do limite / limite, abaixo do qual uma regra se aplica e acima do qual outra regra.

E isso é expresso por ter (e eu poderia argumentar que até o comentário é redundante):

 if (ledgerAmnt >= 500000) {
    //Ledger of 500K or more requires AUTHLDG-1A
    attachDocument("AUTHLDG-1A");
  }

Isso está apenas repetindo o que o código está fazendo:

LEDGER_AMOUNT_REQUIRING_AUTHLDG1A=500000
if (ledgerAmnt >= LEDGER_AMOUNT_REQUIRING_AUTHLDG1A) {
    //Ledger of 500K or more requires AUTHLDG-1A
    attachDocument("AUTHLDG-1A");
}

Observe que o autor assume que o significado de 500000 esteja vinculado a esta regra; não é um valor que é ou é provável que seja reutilizado em outro lugar:

A única alteração de regra de negócios que essa Soft Coding anterior poderia explicar é uma alteração no valor contábil que exigia o formulário AUTHLDG-1A. Qualquer outra alteração nas regras de negócios exigiria ainda mais trabalho - configuração, documentação, código, etc.

O ponto principal do artigo, na minha opinião, é que, às vezes, um número é apenas um número: ele não tem um significado extra além do que é transmitido no código e provavelmente não será usado em outro lugar. Portanto, resumir desajeitadamente o que o código está fazendo (agora) em um nome de variável apenas para evitar valores codificados é repetição desnecessária, na melhor das hipóteses.

Thanos Tintinidis
fonte
2
Se você introduzisse a constante LEDGER_AMOUNT_REQUIRING_AUTHLDG1A, não escreveria mais o comentário no código. Os comentários não são mantidos bem pelos programadores. Se a quantia for alterada, a ifcondição e o comentário ficarão fora de sincronia. Pelo contrário, a constante LEDGER_AMOUNT_REQUIRING_AUTHLDG1Anunca sai de sincronia consigo mesma e explica seu objetivo sem o comentário desnecessário.
ZeroOne
2
@ZeroOne: Exceto que, se a regra de negócios mudar para "Razão de 500K ou mais, exigir AUTHLDG-1A e AUTHLDG-2B", é muito provável que a pessoa que adicionar a attachDocument("AUTHLDG-2B");linha não atualize o nome da constante ao mesmo tempo. Nesse caso, acho que o código é bastante claro, sem um comentário nem uma variável explicativa. (Embora possa fazer sentido ter uma convenção de indicar a seção apropriada do documento de requisitos de negócios por meio de comentários de código. De acordo com essa convenção, um comentário de código que faça isso seria apropriado aqui.)
ruakh
@ruakh, OK, então refatoraria a constante a ser chamada LEDGER_AMOUNT_REQUIRING_ADDITIONAL_DOCUMENTS(o que eu provavelmente deveria ter feito em primeiro lugar). Eu também tenho o hábito de colocar os IDs de requisitos de negócios nas mensagens de confirmação do Git, não no código-fonte.
ZeroOne
1
@ZeroOne: Mas para o AUTHLDG-3C, o valor do razão é realmente o máximo . E para AUTHLDG-4D, a quantidade apropriada do razão depende do estado. (Você já entendeu? Para esse tipo de código, você deseja que ele reflita as regras de negócios, não alguma tentativa de abstração das regras de negócios, porque não há motivo para esperar que a evolução das regras de negócios esteja alinhada com o abstrações que você adotou.)
ruakh 10/04
2
Pessoalmente, não me oponho a colocar o número mágico no código, mas a estruturar o código para que ele precise desses comentários. Se fosse eu, tornaria cada documento uma instância de enum com seu próprio attachIfNecessary()método e apenas repetiria todos eles.
David Moles
8

As outras respostas estão corretas e atenciosas. Mas aqui está a minha resposta curta e doce.

  Rule/value          |      At Runtime, rule/value…
  appears in code:    |   …Is fixed          …Changes
----------------------|------------------------------------
                      |                 |
  Once                |   Hard-code     |   Externalize
                      |                 |   (soft-code)
                      |                 |
                      |------------------------------------
                      |                 |
  More than once      |   Soft-code     |   Externalize
                      |   (internal)    |   (soft-code)
                      |                 |
                      |------------------------------------

Se as regras e os valores especiais aparecerem em um local no código e não forem alterados durante o tempo de execução, codifique como mostrado na pergunta.

Se as regras ou valores especiais aparecerem em mais de um local no código e não forem alterados durante o tempo de execução, faça o soft-code. A codificação suave para uma regra pode definir uma classe / método específico ou usar o padrão Builder . Para valores, codificação eletrônica pode significar definir uma única constante ou enumeração para o valor a ser usado em seu código.

Se as regras ou valores especiais puderem mudar durante o tempo de execução, você deverá externalizá-los. Isso geralmente é feito atualizando valores em um banco de dados. Ou atualize os valores na memória manualmente por um usuário digitando dados. Isso também é feito armazenando valores em um arquivo de texto (XML, JSON, texto sem formatação, o que for) que é varrido repetidamente para a modificação da data e hora da modificação do arquivo.

Basil Bourque
fonte
1
Gosto da sua resposta, mas acho que você também deve considerar se ela muda na implementação. Isto é principalmente relevante se a coisa é um produto que vai ser usado em muitas organizações que podem, por exemplo, têm regras diferentes sobre se um supervisor precisa aprovar um reembolso sobre X, etc etc
Bloke Down The Pub
Concordou com esta resposta e com o comentário sobre implementação. As coisas em que trabalho são implementadas por muitas organizações, e muitas delas têm valores sutilmente diferentes. Nós tendemos a armazenar essas 'configurações' em um banco de dados, e não no arquivo de configuração, mas o princípio é que não queremos criar versões diferentes do nosso software para cada empresa que implementa (em seguida, repita essas versões diferentes a cada atualização) .
RosieC
7

Esta é a armadilha caímos em quando usamos um problema brinquedo e, em seguida, colocar apenas Strawman soluções, quando estamos a tentar ilustrar um problema real.

No exemplo dado, não faz diferença se os valores fornecidos são codificados como valores inline ou definidos como consts.

É o código circundante que tornaria o exemplo um horror de manutenção e codificação. Se não é nenhum código circundante, em seguida, o trecho é bom, pelo menos em um ambiente de refatoração constante. Em um ambiente em que a refatoração tende a não acontecer, os mantenedores desse código já estão mortos, por razões que em breve se tornarão óbvias.

Veja, se houver código em torno dele, coisas ruins acontecem claramente.

A primeira coisa ruim é que o valor 50000 é usado para outro valor em algum lugar, digamos, o valor contábil sobre o qual a taxa de imposto muda em alguns estados ... então, quando a mudança acontece, o mantenedor não tem como saber, quando encontra esses valores. duas instâncias de 50000 no código, se elas significam os mesmos 50k ou 50ks totalmente não relacionados. E você também deve procurar por 49999 e 50001, caso alguém as use também como constantes? Não se trata de chamar essas variáveis ​​em um arquivo de configuração de um serviço separado: mas codificá-las em linha também está claramente errado. Em vez disso, eles devem ser constantes, definidas e com escopo definido na classe ou no arquivo em que são usados. Se as duas instâncias de 50k usam a mesma constante, elas provavelmente representam a mesma restrição legislativa; se não, provavelmente não; e de qualquer forma, eles terão um nome,

Os nomes de arquivos estão sendo passados ​​para uma função - attachDocument () - que aceita nomes de arquivos base como string, sem caminho ou extensão. Os nomes de arquivo são, essencialmente, chaves estrangeiras para algum sistema de arquivos, banco de dados ou para onde o attachDocument () obtém os arquivos. Mas as strings não dizem nada sobre isso - quantos arquivos existem? Quais são os tipos de arquivo? Como você sabe, ao abrir um novo mercado, se precisa atualizar esta função? A que tipos de coisas eles podem ser anexados? O mantenedor é deixado inteiramente no escuro, e tudo o que ele tem é uma string, que pode aparecer várias vezes no código e significar coisas diferentes toda vez que aparece. Em um lugar, "SR008-04X" é um código de trapaça. Noutro, é um comando encomendar quatro foguetes de reforço SR008. Aqui está um nome de arquivo? Eles estão relacionados? Alguém acabou de alterar essa função para mencionar outro arquivo, "CLIENT". Então, você, mantenedor pobre, foi informado que o arquivo "CLIENT" precisa ser renomeado para "CLIENTE". Mas a string "CLIENT" aparece 937 vezes no código ... onde você começa a procurar?

O problema do brinquedo é que os valores são todos incomuns e podem ser razoavelmente garantidos como únicos no código. Não "1" ou "10", mas "50.000". Não é "cliente" ou "relatório", mas "SR008-04X".

O argumento é que a única outra maneira de resolver o problema de constantes impenetrávelmente opacas é inseri-las no arquivo de configuração de algum serviço não relacionado.

Juntos, você pode usar essas duas falácias para provar que qualquer argumento é verdadeiro.

Dewi Morgan
fonte
2
Não é um problema de brinquedo, não é um palhaço. Isso é algo que você verá o tempo todo nesses tipos de aplicativos de negócios. Não há "abertura para um novo mercado", não há reutilização do mesmo número (afinal, isso daria outro significado de qualquer maneira) e, de qualquer forma, o artigo não diz nada contra o DRY - se houver duas dependências no valor, será movido para um método ou uma constante. Mostre exemplos de como essas constantes (das definições de configuração, isso realmente não importa) devem ser nomeadas e onde elas devem ser armazenadas de uma maneira que seja à prova do futuro e mais clara que o código.
Luaan
4
O exemplo não se decompõe porque é um problema de brinquedo. O código circundante sempre será horrível, porque as regras de negócios que o software precisa executar são horrorosas . Tentativas de contornar esse desafio fundamental com mecanismos de regras e DSLs e outros enfeites são muitas vezes procrastinação de programadores , porque resolver problemas de CS é mais agradável do que resolver complexidades de formulários de impostos. As tentativas de alcançar a 'elegância' costumam ser tarefas de tolos, porque a tarefa final do software é modelar um desastre complicado.
Whatsisname
As regras de negócios podem ser horrorosas, mas isso não é uma desculpa para escrever esse tipo de código processual medíocre. (Costumo concordar com Papadimoulis que é mais fácil modelar e manter as regras no código do que na configuração, só acho que deveria ser um código melhor.) O problema DRY que vejo não são os números mágicos, é o repetido if (...) { attachDocument(...); }.
David Moles
2

Existem vários problemas nisso.

Um problema é se um mecanismo de regras deve ser criado para tornar todas as regras facilmente configuráveis ​​fora do próprio programa. A resposta em casos semelhantes a esse geralmente é não. As regras mudarão de maneiras estranhas, difíceis de prever, o que significa que o mecanismo de regras deve ser estendido sempre que houver uma alteração.

Outra questão é como lidar com essas regras e suas alterações no seu controle de versão. A melhor solução aqui é dividir as regras em uma classe para cada regra.

Isso permite que cada regra tenha sua própria validade, algumas regras mudam a cada ano, algumas mudam dependendo da concessão de uma permissão ou da emissão de uma fatura. A própria regra que contém a verificação para qual versão deve ser aplicada.

Além disso, como a constante é privada, ela não pode ser mal utilizada em nenhum outro lugar do código.

Depois, tenha uma lista de todas as regras e aplique a lista.

Uma questão adicional é como lidar com constantes. 500000 pode parecer discreto, mas é preciso muito cuidado para garantir que ela seja convertida corretamente. Se qualquer aritmética de ponto flutuante for aplicada, ela poderá ser convertida em 500.000,00001, portanto, uma comparação com 500.000,00000 poderá falhar. Ou ainda pior, 500000 sempre funciona conforme o esperado, mas, de alguma forma, 565000 falha quando convertido. Verifique se a conversão é explícita e feita por você, não pelo palpite do compilador. Geralmente, isso é feito convertendo-o em algum BigInteger ou BigDecimal antes de ser usado.

Bent
fonte
2

Embora não seja mencionado diretamente na pergunta, gostaria de observar que o importante é não enterrar a lógica de negócios no código.

Código, como no exemplo acima, que codifica os requisitos de negócio especificadas externamente deve realmente vivem em uma parte distinta da árvore de origem, talvez chamado businesslogicou algo semelhante, e os cuidados devem ser tomados para garantir que ele única codifica os requisitos de negócio como forma simples, legível e de forma concisa possível, com um mínimo de clichê e com comentários claros e informativos.

Ele deve não ser misturado com o código "infra-estrutura", que implementa a funcionalidade necessária para realizar a lógica de negócios, como, por exemplo, a implementação do attachDocument()método no exemplo, ou por exemplo UI, registro ou código de banco de dados em geral. Embora uma maneira de impor essa separação seja "codificar" toda a lógica de negócios em um arquivo de configuração, isso está longe de ser o único (ou o melhor) método.

Esse código de lógica de negócios também deve ser escrito com clareza suficiente para que, se você o mostrasse a um especialista em domínio de negócios sem habilidades de codificação, ele seria capaz de entendê-lo. No mínimo, se e quando os requisitos de negócios forem alterados, o código que os codifica deve ser claro o suficiente para que mesmo um novo programador sem família anterior com a base de código possa localizar, revisar e atualizar facilmente a lógica de negócios, assumindo que nenhuma funcionalidade qualitativamente nova é necessária.

Idealmente, esse código também seria escrito em uma linguagem específica de domínio para impor a separação entre a lógica de negócios e a infraestrutura subjacente, mas isso pode ser desnecessariamente complicado para um aplicativo interno básico. Dito isto, se você estiver, por exemplo, vendendo o software para vários clientes, cada um precisando de um conjunto personalizado de regras de negócios, uma linguagem de script simples e específica de domínio (talvez, por exemplo, com base em um sandbox Lua ) pode ser a coisa certa .

Ilmari Karonen
fonte
Era exatamente isso que eu estava pensando !!! Quando a lógica está profundamente envolvida no código, como um especialista em domínio / assunto ou usuário de negócios pode ver os valores e a lógica que estão em uso para garantir que eles estejam certos e diagnosticar o comportamento do sistema? Uma coisa que um arquivo de configuração faz é tornar as configurações visíveis . É necessário que haja alguns meios para promover a visibilidade das regras de negócios - mesmo que isso torne a codificação "mais difícil". Posso aceitar uma classe fina ou um conjunto de classes que fazem o trabalho, sem misturar outras preocupações - desde que o usuário comercial tenha os meios para acessá-las e compreendê-las.
ErikE