Resumo: Existe um bom padrão de design para reduzir a duplicação de informações entre valores fortemente interdependentes?
Na minha linha de trabalho, é bastante comum ter um relacionamento entre quantidades, de modo que você possa derivar uma das quantidades se conhecer as outras. Um exemplo poderia ser a lei dos gases ideais :
Pv = RT
Você pode imaginar criar uma classe para representar o estado de um gás ideal. A classe teria 3 propriedades, naturalmente Pressure
, Temperature
e SpecificVolume
cada um de um tipo apropriado.
Para o usuário de um objeto dessa classe, parece natural esperar que, se você definir valores para ambos Pressure
e Temperature
, poderá ler um valor para SpecificVolume
e esperar que o objeto calcule isso para você.
Da mesma forma, se você definir valores para ambos Pressure
e SpecificVolume
, poderá ler Temperature
, etc.
Para realmente implementar essa classe, no entanto, é necessária alguma duplicação de informações. Você precisaria programar explicitamente todas as variações da equação, tratando uma variável diferente como dependente em cada caso:
T = P * v / R
P = R * T / v
v = R * T / P
o que parece violar o princípio DRY . Embora cada um expresse a mesma relação, esses casos requerem codificação e teste independentes.
Em casos reais, a lógica em que estou pensando é mais complexa que este exemplo, mas apresenta o mesmo problema básico. Portanto, haveria um valor real se eu pudesse expressar a lógica apenas uma vez, ou pelo menos menos vezes.
Observe que uma classe como essa provavelmente também teria que lidar para garantir que fosse inicializada corretamente antes que os valores fossem lidos, mas acho que essa é uma consideração secundária.
Embora eu tenha dado um exemplo matemático, a questão não se limita apenas às relações matemáticas entre os dados. Isso apenas parecia ser um exemplo simples de entender.
fonte
Pv/T != R
. Não tenho certeza se essa ideia pode ajudá-lo a resolver os problemas subjacentes ou se isso ainda a complica.Respostas:
Não há problema da perspectiva do OO. Você poderia ter uma classe estática que oferece métodos GasLaw
et cetera
e não haveria um problema de duplicação. Não há objeto, nem membros de dados. Apenas comportamentos que são todos diferentes.
Você pode considerar a lei do gás como "uma coisa", mas as operações são todas distintas. Não há nada errado em ter um método para cada um dos casos de uso da lei.
fonte
Isso não é realmente possível da maneira que você pergunta.
Considere: eu tenho um objeto de gás ideal
g
. Se eu definir explicitamente todas as três temperaturas, pressão e volume específico e, em seguida, obtiver a temperatura novamente, como:deveria:
t1
eu originalmente defini?Se você precisar fazer isso, precisará rastrear muito estado para cada membro: ele não foi inicializado, definido explicitamente pelo código do cliente ou armazenando em cache um valor calculado? O valor explicitamente definido ou em cache foi invalidado por outro membro que está sendo definido posteriormente?
Obviamente, é difícil prever o comportamento da leitura do código do cliente. Esse é um design ruim, porque sempre será difícil descobrir por que você recebeu a resposta. A complexidade é um sinal de que esse problema é inadequado para OO, ou pelo menos que um objeto de gás ideal com estado é uma má escolha de abstração.
fonte
Primeiro, acho que você não viola o princípio DRY, porque as três fórmulas calculam valores diferentes. O fato de ser uma dependência entre todos os 3 valores é porque você a vê como uma equação matemática e não como uma atribuição de variável programática.
Sugiro implementar seu caso com uma classe imutável IdealGas da seguinte maneira
Deixe-me explicar a implementação. A classe IdealGas encapsulou as 3 propriedades Pressão, Temperatura e Volume Específico como valores finais para imutabilidade. Toda vez que você chamar getXXX, nenhum cálculo será feito. O truque é ter 3 construtores para todas as 3 combinações dos 2 parâmetros dados. Em todo construtor, você calcula a terceira variável ausente. A computação é feita uma vez, no tempo de construção e atribuída ao terceiro atributo. Como isso é feito no construtor, o terceiro atributo pode ser final e imutável. Para imutabilidade, presumo que as classes Pressure, Temperature e SpecificVolume também sejam imutáveis.
A única parte restante é ter getters para todos os atributos. Não existem setters porque você passa parâmetros para construtores. Se você precisar alterar um atributo, crie uma nova instância IdealGas com os parâmetros desejados.
No meu exemplo, as classes Pressure, Temperature e SpecificVolume são invólucros simples de um valor duplo. O código de amostra está em java, mas pode ser generalizado.
Essa abordagem pode ser generalizada, passar todos os dados relacionados para um construtor, computar dados relacionados no construtor e ter apenas getters.
fonte
Esta é uma pergunta realmente interessante! Você está correto em princípio ao duplicar informações porque a mesma equação é usada nos três casos, apenas com diferentes incógnitas.
Mas em uma linguagem de programação convencional típica, não há suporte interno para resolver equações. As variáveis na programação são sempre quantidades conhecidas (no momento da execução); portanto, apesar das semelhanças superficiais, as expressões nas linguagens de programação não são comparáveis às equações matemáticas.
Em um aplicativo típico, uma equação como você descreve apenas seria escrita como três expressões separadas e você vive com a duplicação. Mas as bibliotecas de solução de equações existem e podem ser usadas nesse caso.
Isso é mais do que um padrão, porém, é um paradigma de programação completo, chamado de solução de restrições, e existem linguagens de programação dedicadas como o Prolog para esse tipo de problema.
fonte
Isso é uma irritação comum ao codificar modelos matemáticos no software. Ambos usam uma notação muito semelhante para modelos simples, como a lei dos gases, mas as linguagens de programação não são matemática e muitas coisas que você pode deixar de fora enquanto trabalha no mundo da matemática precisam ser explicitamente explicadas em software.
Não está se repetindo. Não há conceito de equação, transformação algébrica ou "resolução de x" na maioria das linguagens de programação. Sem uma representação de software de todos esses conceitos, não há como chegar ao software,
T = P * v / R
dada aPv = RT
equação.Embora possa parecer uma limitação irracional das linguagens de programação quando se olha para modelos simples como a lei dos gases, mesmo as matemáticas um pouco mais avançadas permitem equações que não têm soluções algébricas fechadas ou nenhum método conhecido que possa derivar a solução com eficiência. Para a arquitetura de software, você não pode depender dela no caso mais complexo.
E o caso simples por si só? Não tenho certeza de que valeria a pena ter que ler a especificação do recurso na linguagem de programação. Os modelos tendem a ser substituídos, não modificados e geralmente têm apenas algumas variáveis a serem resolvidas.
fonte
Não, dois são suficientes. Mas fazê-los privados e expor métodos como
pressure()
,temperature()
, especificVolume()
. Um deles, que não corresponde às duas propriedades particulares, deve ter a lógica apropriada. Assim, podemos eliminar a duplicação de dados.Deve haver três construtores com parâmetros
(P, T)
,(P, v)
e(T, v)
. Um deles, correspondente às duas propriedades privadas, simplesmente atua como setter. Outros dois devem ter a lógica apropriada. É claro que a lógica é escrita três vezes (duas vezes aqui e uma vez no parágrafo anterior), mas elas não são duplicadas. Mesmo que você os considere duplicados, eles são necessários.Sim matematicamente e não OO-ly. Nos objetos OO, os cidadãos de primeira classe não são expressões. Para fazer expressões cidadãos de primeira classe (ou estabelecer o relacionamento expresso pela mesma coisa), precisamos escrever uma classe, digamos,
Expression
ouEquation
que não seja uma tarefa fácil (pode haver algumas bibliotecas).O relacionamento também não é um cidadão de primeira classe. Para isso, podemos precisar escrever uma classe
Relationship
que também não seja fácil. Mas viver com duplicatas é mais fácil.Se você decidir viver com duplicatas e se os parâmetros no relacionamento forem altos, considere usar o padrão Construtor . Mesmo se os parâmetros forem menos, considere o uso de Static Factories para evitar limitações de sobrecarga do construtor.
fonte
Uma classe deve representar o comportamento de um gás ideal. Uma instância de classe - um objeto - representa o estado.
Isso não é pegar lêndeas. O design da classe deve ser de uma perspectiva de comportamento e funcionalidade. O comportamento é exposto publicamente, de modo que um objeto instanciado (sim, isso é supérfluo) atinge um estado através do exercício do comportamento.
Assim:
E o seguinte é simplesmente absurdo e impossível no mundo real. E não faça isso no código:
O comportamento torna a pergunta "informações duplicadas" discutível
Afetar as mesmas propriedades de estado por métodos diferentes não é duplicação. O seguinte também afeta a temperatura, mas não é uma duplicata do acima:
fonte
Possivelmente, você pode usar a geração de código para gerar as várias formas da equação a partir da única forma especificada.
Posso imaginar que seria bastante complicado para fórmulas mais complexas. Mas para o simples que você dá, você só precisa
Eu posso imaginar se você adicionar multiplicação, adição e subtração a essa lista e sua fórmula base tiver cada variável apenas uma vez, então você poderá usar a manipulação de cadeias para gerar automaticamente todas as versões da fórmula com o código apropriado para usar a correta, dadas as variáveis conhecidas .
O Wolfram Alpha possui várias ferramentas de manipulação de equações, por exemplo.
Contudo!! a menos que você tenha grandes listas dessas classes para produzir, não vejo esse código como um uso eficaz do seu tempo.
Você só precisa gerar esse código uma vez por função e é provável que suas funções sejam muito complexas para serem resolvidas da maneira mais simples que no seu exemplo.
Manualmente codificação de cada versão é provável que seja o método mais rápido e mais confiável de gerar as funções e embora você pode imaginar uma solução onde você código para iteratively acho valores e teste de verdade, eu acho que você fazer necessidade de gerar e compilar as funções para calcular a variável desconhecida com uma quantidade sensata de poder de processamento.
fonte
Acho que você está pensando demais sobre isso, considere a seguinte solução:
Você só precisa definir uma função, sem duplicação, você pode até usá-lo para uma implementação de classe interna, ou você pode apenas usar essa função, apenas alterar quais parâmetros você usa onde.
Você também pode usar esse mesmo padrão para qualquer situação em que você tenha uma função na qual os valores sejam trocados.
Se você estiver usando um idioma estatístico e tiver que lidar com uma função em que os valores possam ser de tipos diferentes, poderá usar a programação de modelos (observe a sintaxe do C ++)
fonte