Eu tenho um valor que muitos objetos precisam. Por exemplo, um aplicativo financeiro com diferentes investimentos como objetos, e a maioria deles precisa da taxa de juros atual.
Eu esperava encapsular meu "ambiente financeiro" como um objeto, com a taxa de juros como uma propriedade. Porém, objetos irmãos que precisam desse valor não conseguem alcançá-lo.
Então, como compartilho valores entre muitos objetos sem sobrecarregar meu design? Obviamente, estou pensando nisso errado.
object-oriented
Greg
fonte
fonte
update
função chamada a cada passo do tempo? Você pode postar em pseudocódigo como sua simulação opera?Singleton
é um mundo com açúcar sintático de OO e é uma solução terrível que une firmemente seu código das piores formas possíveis. Leia este artigo várias vezes até que você o entenda!DateTime
como entrada e retorna um número como saída.Respostas:
Este é um cheiro de design. É incomum que muitos objetos precisem saber sobre algo. Dito isto, a taxa de juros atual é um bom exemplo de circunstâncias excepcionais. Uma coisa que se preocupar é que raramente há a taxa de juros. Instrumentos financeiros diferentes usam taxas diferentes. No mínimo, localidades diferentes usam taxas 'padrão' diferentes. Além disso, para ajudar nos testes e nos relatórios, geralmente você deseja aprovar uma taxa, já que não deseja usar a taxa atual . Você deseja usar a taxa 'e se' ou 'na data do relatório'.
Ao compartilhá-los, não tê-los todos se refere a uma única instância. Passar a mesma coisa ainda é um acoplamento, até certo ponto, mas não muito, já que algo como a taxa de juros atual é necessária como entrada para uma variedade de cálculos.
fonte
Nesse caso em particular, eu usaria o padrão Singleton . O FinancialEnvironment seria o objeto que todas as outras bibliotecas de classes conhecem, mas seria instanciado pelo Singleton. Idealmente, você enviaria esse objeto instanciado para as várias bibliotecas de classes.
Por exemplo:
As outras classes são unidas apenas através da biblioteca de classes Entities, elas aceitam um objeto FinancialEnvironment instanciado. Eles não se importam como foi criado, apenas a camada de serviço, tudo o que eles querem é a informação. O singleton também pode ser inteligente o suficiente para armazenar vários objetos FinancialEnvironment, dependendo das regras para o local, como @Telastyn apontou.
Em uma nota lateral, eu não sou um grande fã do Padrão Singleton, considero um cheiro de código, pois pode ser mal utilizado com muita facilidade. Mas, em alguns casos, você precisa disso.
Atualizar:
Se você absolutamente, positivamente deve ter uma variável global, a implementação do Padrão Singleton, conforme descrito acima, funcionaria. No entanto, não sou um grande fã disso e, com base nos comentários do meu post original, várias outras pessoas também não são. Como algo tão volátil como uma Taxa de juros, um Singleton pode não ser a melhor solução. Singletons funcionam melhor quando a informação não muda. Por exemplo, usei um Singleton em um dos meus aplicativos para instanciar contadores de desempenho. Porque se eles mudarem, você deverá ter uma lógica para lidar com os dados que estão sendo atualizados.
Se eu fosse um apostador, apostaria que a taxa de juros foi armazenada em algum lugar do banco de dados ou recuperada por meio de um serviço da web. Nesse caso, um Repositório (camada de acesso a dados) seria recomendado para recuperar essas informações. Para evitar viagens desnecessárias ao banco de dados (não sei com que frequência as taxas de juros são alteradas ou outras informações na classe FinancialInformation), o cache pode ser usado. No mundo C #, a biblioteca Caching Application Block da Microsoft funciona muito bem.
A única coisa que mudaria a partir do exemplo acima, seriam as várias classes na camada de serviço que precisam que a FinancialInformation recuperasse da Camada de Acesso a Dados, em vez de o Singleton instanciar o objeto.
fonte
Singleton
é uma empresa global com açúcar sintático OO e uma muleta para os preguiçosos e de mente fraca.Singleton/global
é a pior maneira absoluta de associar firmemente seu código a algo que será um câncer mais tarde, quando você perceber que idéia colossalmente ruim era e por que todo mundo diz que é!Arquivos de configuração?
Se você possui valores usados "globalmente", coloque-os em um arquivo de configuração. Então cada sistema e subsistema pode fazer referência a isso e puxar as chaves necessárias, torná-las somente leitura.
fonte
Estou falando da experiência de quem tem cerca de um mês de manutenção em um projeto de bom tamanho (~ 50k LOC) que acabamos de lançar.
Posso lhe dizer que você provavelmente não quer realmente um objeto global. A introdução desse tipo de lixo fornece muito mais oportunidades de abuso do que ajuda.
Minha sugestão inicial é que, se você tem várias classes diferentes que precisam de uma taxa de juros atual, provavelmente deseja que elas implementem um
IInterestRateConsumer
ou algo assim. Dentro dessa interface, você terá umaSetCurrentInterestRate(double rate)
(ou o que fizer sentido), ou talvez apenas uma propriedade.Passar uma taxa de juros não é realmente um acoplamento - se sua classe precisa de uma taxa de juros, isso faz parte da API. É apenas um acoplamento se uma de suas classes começar a se preocupar exatamente com como a outra classe usa essa taxa de juros.
fonte
Martin Fowler tem um artigo que fala brevemente sobre como refatorar uma global estática para algo mais flexível. Basicamente, você o transforma em um singleton e modifica o singleton para que ele suporte a substituição da classe da instância por uma subclasse (e, se necessário, mova a lógica que cria a instância para uma classe separada que pode ser subclassificada, o que você faria se criar a instância de superclasse, substituí-la mais tarde for um problema).
Obviamente, você precisa pesar os problemas com singletons (até singletons substituíveis) versus a dor de passar o mesmo objeto em qualquer lugar.
No que diz respeito ao objeto "ambiente financeiro" - é conveniente programar na primeira passagem, mas ao concluir, você adicionou algumas dependências extras. As classes que agora precisam apenas de uma taxa de juros só funcionam quando passam por um objeto do ambiente financeiro, o que dificulta sua reutilização quando não houver um objeto do ambiente financeiro. Então eu desencorajaria a aprovação amplamente.
fonte
Por que não colocar os dados da taxa de juros em um cache central?
Você pode usar uma das várias bibliotecas de cache, o que melhor se adequar aos seus requisitos, algo como o memcached resolve todos os seus problemas de simultaneidade e gerenciamento de código e permitirá que seu aplicativo seja escalado para vários processos.
Ou vá até o porco e armazene-os em um banco de dados, o que permitirá que você dimensione para vários servidores.
fonte
Em tais situações, introduzi (reutilizei) o termo "contexto" com êxito, às vezes com várias camadas.
Isso significa um singleton, portanto, um armazenamento de objetos "global", do qual esses tipos de objetos podem ser solicitados. Os códigos que os exigem, incluem o cabeçalho da loja e usam as funções globais para obter suas instâncias de objeto (como agora, o provedor de taxa de juros).
A loja pode ser:
Quanto maior o sistema, mais utilizável é a última solução, com um risco bem pequeno de usar a enumeração errada. Por outro lado, com idiomas que permitem declarações de tipo encaminhamento, acho que você pode usar acessadores digitados sem incluir todos os cabeçalhos da loja.
Mais uma observação: você pode ter várias instâncias do mesmo tipo de objeto para usos diferentes, como valor de idioma às vezes diferente para a GUI e para registros de impressão, globais e no nível da sessão, etc. , mas a função da instância solicitada (CurrentInterestRate).
Na implementação da loja, você precisa gerenciar os níveis de contexto e as coleções de instâncias de contexto. Um exemplo simples é o serviço web, onde você tem o contexto global (uma instância para todas as solicitações para esse objeto - problemático ao ter um farm de servidores) e um contexto para cada sessão da web. Você também pode ter contextos para cada usuário, que pode ter várias sessões paralelas, etc. Com vários servidores, você deve usar um tipo de cache distribuído para essas coisas.
Quando a solicitação é recebida, você decide qual nível de contexto é o objeto solicitado e obtém esse contexto para a chamada. Se o objeto estiver lá, você o envia de volta; caso contrário, crie e armazene-o nesse nível de contexto e retorne-o. Obviamente, sincronize a seção de criação (e publique-a no cache distribuído). A criação pode ser configurável como plug-in, melhor com linguagens que permitem criar instâncias de objetos pelo nome da classe (Java, Objective C, ...), mas você pode fazer isso em C, bem como em bibliotecas conectáveis com funções de fábrica.
Nota lateral: o chamador NÃO deve saber muito sobre seus próprios contextos e o nível de contexto do objeto solicitado. Razões: 1: é fácil cometer erros (ou "truques inteligentes") jogando com esses parâmetros; 2: o nível de contexto do solicitado pode mudar mais tarde. Eu conecto principalmente informações de contexto ao encadeamento, para que o armazenamento de objetos tenha as informações sem parâmetros extras da solicitação.
Por outro lado, a solicitação pode conter dicas para a instância: como obter taxa de juros para uma data específica. Deve ser o mesmo acesso "global", mas várias instâncias, dependendo da data (e levando valores diferentes de data para a mesma instância entre alterações de taxa), portanto, é recomendável adicionar um objeto "dica" à solicitação, usado pelo fábrica de instância e não a loja; e um keyForHint para a fábrica, usado pela loja. Você pode adicionar essas funções mais tarde, apenas mencionei.
Para o seu caso, isso é um tipo de exagero (apenas um objeto é servido no nível global), mas para um código extra bem pequeno e simples no momento, você obtém um mecanismo para requisitos adicionais, talvez mais complexos.
Outra boa notícia: se você está em Java, obtém esse serviço do Spring sem pensar muito, só queria explicar em detalhes.
fonte
O motivo para NÃO usar um global (ou singleton) é que, embora inicialmente você espere ter apenas um valor, muitas vezes é surpreendentemente útil poder usar o mesmo código várias vezes no mesmo programa, por exemplo:
Eu faria da taxa de juros um membro da classe "instrumento financeiro" e aceitaria que você a passasse para qualquer classe de membro (por cálculo ou dando a eles um ponteiro / gancho na construção).
fonte
As coisas devem ser passadas em mensagens, não lidas de uma coisa global.
fonte