Tengo un valor que muchos objetos necesitan. Por ejemplo, una aplicación financiera con diferentes inversiones como objetos, y la mayoría de ellas necesitan la tasa de interés actual.
Esperaba encapsular mi "entorno financiero" como un objeto, con la tasa de interés como propiedad. Pero, los objetos hermanos que necesitan ese valor no pueden alcanzarlo.
Entonces, ¿cómo comparto valores entre muchos objetos sin acoplar demasiado mi diseño? Obviamente estoy pensando en esto mal.
object-oriented
Greg
fuente
fuente
update
función que se llama en cada paso de tiempo? ¿Puedes publicar en pseudocódigo cómo funciona tu simulación?Singleton
es un global con azúcar sintáctico OO y es una solución terrible que combina estrechamente su código en algunas de las peores formas posibles. ¡Lee este artículo una y otra vez hasta que lo entiendas!DateTime
como entrada y devuelve un número como salida.Respuestas:
Este es un olor de diseño. Es raro que muchos objetos necesiten saber sobre algo. Dicho esto, la tasa de interés actual es un ejemplo bastante bueno de circunstancias excepcionales. Una cosa de la que preocuparse es que rara vez existe la tasa de interés. Diferentes instrumentos financieros utilizan diferentes tasas. Como mínimo, las diferentes configuraciones regionales utilizan diferentes tasas 'estándar'. Además, para ayudar en las pruebas y los informes, generalmente querrá pasar una tasa ya que no desea usar la tasa actual allí. Desea utilizar la tasa 'qué pasaría si' o 'a partir de la fecha del informe'.
Al compartirlos, no hacer que todos se refieran a una sola instancia. Pasar lo mismo sigue siendo un acoplamiento, pero no un exceso , ya que se necesita algo así como la tasa de interés actual como entrada para una variedad de cálculos.
fuente
En este caso particular, usaría el patrón Singleton . El entorno financiero sería el objeto que todas las otras bibliotecas de clases conocen, pero sería instanciado por Singleton. Lo ideal sería enviar ese objeto instanciado a las diversas bibliotecas de clases.
Por ejemplo:
Las otras clases solo están vinculadas entre sí a través de la biblioteca de clases Entidades, aceptan un objeto FinancialEnvironment instanciado. No les importa cómo se creó, solo lo hace la capa de servicio, todo lo que quieren es la información. El singleton también podría ser lo suficientemente inteligente como para almacenar varios objetos del entorno financiero, dependiendo de las reglas para el local, como señaló @Telastyn.
En una nota al margen, no soy un gran admirador del Patrón Singleton, lo considero un olor a código, ya que se puede usar mal con mucha facilidad. Pero en algunos casos lo necesitas.
Actualizar:
Si absolutamente, debe tener una variable global, entonces implementar el Patrón Singleton como se describió anteriormente funcionaría. Sin embargo, no soy un gran admirador de esto, y según los comentarios de mi publicación original, muchas otras personas tampoco lo son. Como algo tan volátil como una tasa de interés, un Singleton puede no ser la mejor solución. Los Singleton funcionan mejor cuando la información no cambia. Por ejemplo, usé un Singleton en una de mis aplicaciones para crear instancias de contadores de rendimiento. Porque si cambian, entonces debe tener una lógica para manejar los datos que se actualizan.
Si fuera un apostador, apostaría que la tasa de interés se almacenó en algún lugar de una base de datos, o se recuperó a través de un servicio web. En ese caso, se recomendaría un Repositorio (capa de acceso a datos) para recuperar esa información. Para evitar viajes innecesarios a la base de datos (no estoy seguro con qué frecuencia cambian las tasas de interés u otra información en la clase de Información Financiera), se podría utilizar el almacenamiento en caché. En el mundo C #, la biblioteca Caching Application Block de Microsoft funciona muy bien.
Lo único que cambiaría del ejemplo anterior, serían las diversas clases en la capa de servicio que necesitan que FinancialInformation recupere de la capa de acceso a datos en lugar de que Singleton instancia el objeto.
fuente
Singleton
es un producto global con azúcar sintáctico OO y una muleta para los vagos y débiles.Singleton/global
¡es la peor manera absoluta de acoplar estrechamente su código a algo que será un cáncer más adelante cuando se dé cuenta de lo colosalmente mala idea que era y por qué todos dicen que lo son!¿Archivos de configuración?
Si tiene valores que se usan "globalmente", colóquelos en un archivo de configuración. Luego, cada sistema y subsistema puede hacer referencia a esto y extraer las teclas necesarias, hacerlas de solo lectura.
fuente
Estoy hablando de la experiencia de alguien que tiene aproximadamente un mes de mantenimiento en un proyecto de buen tamaño (~ 50k LOC) que acabamos de lanzar.
Puedo decirte que probablemente no quieras un objeto global. La introducción de ese tipo de cruft ofrece muchas más oportunidades de abuso de lo que ayuda.
Mi sugerencia inicial es que si tienes varias clases diferentes que necesitan una tasa de interés actual, entonces probablemente solo quieras que implementen
IInterestRateConsumer
algo o algo. Dentro de esa interfaz tendrás unSetCurrentInterestRate(double rate)
(o lo que tenga sentido), o tal vez solo una propiedad.Pasar una tasa de interés no es en realidad un acoplamiento: si su clase necesita una tasa de interés, eso es parte de su API. Solo se acopla si una de sus clases comienza a preocuparse por cómo exactamente la otra clase usa esa tasa de interés.
fuente
Martin Fowler tiene un artículo que habla brevemente sobre cómo refactorizar un global estático en algo más flexible. Básicamente, lo convierte en un singleton y luego modifica el singleton para que admita anular la clase de la instancia con una subclase (y, si es necesario, mueva la lógica que crea la instancia a una clase separada que puede subclasificarse, lo que haría si crea la instancia de superclase, reemplazarla más tarde es un problema).
Por supuesto, debe sopesar los problemas con los singletons (incluso los singletons sustituibles) frente al dolor de pasar el mismo objeto a todas partes.
En cuanto al objeto "entorno financiero", es conveniente programar en el primer paso, pero cuando haya terminado, habrá agregado algunas dependencias adicionales. Las clases que solo necesitan una tasa de interés ahora solo funcionan cuando se pasa un objeto del entorno financiero, lo que dificultará su reutilización cuando no tenga un objeto del entorno financiero por ahí. Así que desalentaría pasarlo ampliamente.
fuente
¿Por qué no poner los datos de la tasa de interés en un caché central?
Puede usar una de varias bibliotecas de caché, lo que mejor se adapte a sus necesidades, algo como memcached resuelve todos sus problemas de concurrencia y gestión de código y permitirá que su aplicación se adapte a múltiples procesos.
O vaya por completo y guárdelos en una base de datos, que le permitirá escalar a múltiples servidores.
fuente
En tales situaciones, he introducido (reutilizado) con éxito el término "contexto" con a veces múltiples capas.
Esto significa un singleton, por lo tanto, un almacén de objetos "global", desde el cual se puede solicitar este tipo de objetos. Los códigos que los requieren, incluyen el encabezado de la tienda y usan las funciones globales para obtener sus instancias de objeto (como ahora, el proveedor de tasas de interés).
La tienda puede ser:
Cuanto más grande es el sistema, más utilizable es la última solución, por un riesgo bastante pequeño de usar la enumeración incorrecta. Por otro lado, con idiomas que permiten declaraciones de tipo de reenvío, creo que puede usar los accesos escritos sin incluir todos los encabezados de la tienda.
Una nota más: puede tener varias instancias del mismo tipo de objeto para diferentes usos, como a veces un valor de idioma diferente para la GUI y para la impresión, registros globales y de nivel de sesión, etc., por lo que el nombre de enumeración / acceso NO debe reflejar el tipo real , pero el rol de la instancia solicitada (CurrentInterestRate).
En la implementación de la tienda, debe administrar los niveles de contexto y las colecciones de instancias de contexto. Un ejemplo simple es el servicio web, donde tiene el contexto global (una instancia para todas las solicitudes de ese objeto, problemático cuando se tiene una granja de servidores) y un contexto para cada sesión web. También puede tener contextos para cada usuario, que puede tener múltiples sesiones paralelas, etc. Con múltiples servidores, debe usar una especie de caché distribuida para tales cosas.
Cuando llega la solicitud, usted decide qué nivel de contexto es el objeto solicitado, obtenga ese contexto para la llamada. Si el objeto está allí, lo devuelve; si no, lo crea y lo almacena en ese nivel de contexto, y lo devuelve. Por supuesto, sincronice la sección de creación (y publíquela en el caché distribuido). La creación puede ser configurable como un complemento, mejor con lenguajes que permiten crear instancias de objetos por su nombre de clase (Java, Objective C, ...), pero puede hacerlo en C también con bibliotecas conectables que tienen funciones de fábrica.
Nota al margen: la persona que llama NO debe saber demasiado sobre sus propios contextos y el nivel de contexto del objeto solicitado. Motivos: 1: es fácil cometer errores (o "trucos ingeniosos") jugando con estos parámetros; 2: el nivel de contexto de lo solicitado podría cambiar más adelante. Principalmente conecto información de contexto al hilo, por lo que el almacén de objetos tiene la información sin parámetros adicionales de la solicitud.
Por otro lado, la solicitud puede contener una pista para la instancia: como obtener la tasa de interés para una fecha específica. Debe ser el mismo acceso "global", pero múltiples instancias dependiendo de la fecha (y llevando diferentes valores de fecha a la misma instancia entre cambios de velocidad), por lo que es aconsejable agregar un objeto "sugerencia" a la solicitud, utilizado por el instancia de fábrica y no la tienda; y un keyForHint para la fábrica, utilizado por la tienda. Puede agregar estas funciones más tarde, acabo de mencionar.
Para su caso, esto es una especie de exageración (solo se sirve un objeto a nivel global), pero para un código adicional bastante pequeño y simple en este momento, obtiene un mecanismo para requisitos adicionales, quizás más complejos.
Otra buena noticia: si estás en Java, obtienes este servicio de Spring sin pensar demasiado, solo quería explicarte en detalle.
fuente
La razón para NO usar un global (o singleton) es que, aunque inicialmente espera tener solo un valor, a menudo es sorprendentemente útil poder usar el mismo código varias veces en el mismo programa, por ejemplo:
Convertiría la tasa de interés en un miembro de la clase de "instrumento financiero" y aceptaría que tiene que pasarla a cualquier clase de miembro (ya sea por cálculo o dándoles un puntero / gancho en la construcción).
fuente
Las cosas deben pasarse en mensajes, no leerse de una cosa global.
fuente