design patterns - Monostate vs. Singleton
design-patterns language-agnostic (5)
Alguien debería notar que los singletons y los monostate son patrones extremadamente peligrosos. Tienden a ser mal utilizados por los codificadores perezosos que no quieren tener que pensar en la vida del objeto que quieren convertir en un singleton. Hacen que las pruebas sean más difíciles y crean sistemas inflexibles que están estrechamente ligados.
Es extremadamente raro encontrar una situación en la que realmente se necesite un singleton o monoestaño. El método preferido de colaboración de objetos es Dependency Injection.
Se ha escrito mucho sobre esto:
¿Cuáles son los escenarios en los que uno usaría un patrón Monostate en lugar de singleton inorder para mantener un objeto global?
Editar: sé lo que son los patrones de Singleton y Monostate. También he implementado Singleton en bastantes escenarios. Solo quiero saber los escenarios (ejemplos de casos) donde el patrón MonoState necesita ser implementado.
Por ej. Necesito mantener una lista de columnas por pantalla en mi aplicación de formularios de Windows. Podría usar un diccionario de Singleton en este caso. Sin embargo, estoy almacenando una lista en la var global estática y quería proporcionar indizadores (ya que necesito agregar dinámicamente una nueva entrada a la lista si la clave no está presente) donde podría especificar ScreenDetails.ScreenName como clave y obtener la pantallaDetalles .ColumnsTable. Como los indexadores no pueden operar en una clase estática, cambié el patrón a Monostate.
Entonces me gustaría saber qué otros escenarios pueden obligar a un usuario a usar Monostate en lugar de Singletons.
En su base Monostate es solo azúcar sintáctico alrededor de Singleton. Donde Monótate se pone interesante es cuando comienzas a crear subclases, porque las subclases pueden decorar el estado compartido con un comportamiento diferente.
Un ejemplo simple, aunque algo artificial y no muy eficiente :):
public class GlobalTable implements Iterable<Key> {
/** Shared state -- private */
private static final Map<Key, Value> MAP = new LinkedHashMap<Key, Value>();
/** Public final accessor */
public final Value get(Key key) {
return MAP.get(key);
}
/** Public final accessor */
public final boolean put(Key key, Value value) {
return MAP.put(key);
}
/** Protected final accessor -- subclasses can use this to access
the internal shared state */
protected final Set<Key> keySet() {
return MAP.keySet();
}
/** Virtual -- subclasses can override for different behavior */
public Iterator<Key> iterator() {
return Collections.unmodifiableSet(MAP.keySet()).iterator();
}
}
Ahora, ¿y si queremos acceso indexado?
public class IndexedGlobalTable extends GlobalTable {
public List<Key> getKeysAsList() {
return Collections.unmodifiableList(new ArrayList<Key>(keySet()));
}
public Key getKeyAt(int index) {
return getKeysAsList().get(index);
}
public Value getValueAt(int index) {
return get(getKeyAt(index));
}
}
¿Qué hay de las claves ordenadas?
public class SortedGlobalTable extends GlobalTable {
@Override
public Iterator <Key> iterator() {
return Collections
.unmodifiableSortedSet(new TreeSet<Key>(keySet())).iterator();
}
}
Cada vez que necesita una u otra vista de los datos, solo crea una instancia de la subclase adecuada.
Por supuesto, si los datos globales son realmente una buena idea, en primer lugar, es otra pregunta, pero al menos Monostate le da más flexibilidad en la forma de usarlo.
Esto es lo que Robert C. Martin tiene que decir al respecto: Singleton vs. Monostate (pdf)
SINGLETON se utiliza mejor cuando tiene una clase existente que desea restringir mediante derivación, y no le molesta que todos tengan que llamar al método instance () para obtener acceso. Monostate se utiliza mejor cuando se desea que la naturaleza singular de la clase sea transparente para los usuarios o cuando se desean emplear derivadas polimórficas de un solo objeto.
La diferencia entre los dos patrones es una de comportamiento vs. estructura. El patrón SINGLETON impone la estructura de la singularidad. Impide que se cree más de una instancia. Mientras que MONOSTATE impone el comportamiento de la singularidad sin imponer restricciones estructurales.
Beneficios del SINGLETON
- Aplicable a cualquier clase. Puede cambiar cualquier clase en un SINGLETON simplemente haciendo sus constructores privados y agregando las funciones estáticas y variables apropiadas.
- Se puede crear a través de la derivación. Dada una clase, puede crear una subclase que sea SINGLETON.
- Evaluación floja. Si el SINGLETON nunca se usa, nunca se crea.
Costos del SINGLETON
La destrucción no está definida. No hay una buena manera de destruir o desmantelar un SINGLETON. Si agrega un método de desactivación que anula la instancia, es posible que otros módulos en el sistema mantengan una referencia a la instancia de SINGLETON. Las llamadas posteriores a Instancia provocarán la creación de otra instancia, lo que provocará la existencia de dos instancias simultáneas.
No heredado Una clase derivada de un SINGLETON no es un singleton. Si necesita ser un SINGLETON, la función estática y la variable deben agregarse.
Eficiencia. Cada llamada a instancia invoca la sentencia if. Para la mayoría de esas llamadas, la declaración if es inútil.
No transparente. Los usuarios de un SINGLETON saben que están usando un SINGLETON porque deben invocar el método de Instancia.
Beneficios de MONOSTATE
Transparencia. Los usuarios de un MONOSTATE no se comportan de manera diferente que los usuarios de un objeto regular. Los usuarios no necesitan saber que el objeto es MONOSTATE.
Derivabilidad. Los derivados de un MONOSTATO son MONOSATES. De hecho, todos los derivados de un MONOSTATO son parte del mismo MONOSTATO. Todos comparten las mismas variables estáticas.
Polimorfismo. Como los métodos de un MONOSTATE no son estáticos, se pueden anular en una derivada. Por lo tanto, diferentes derivados pueden ofrecer un comportamiento diferente sobre el mismo conjunto de variables estáticas.
Creación y destrucción bien definidas. Las variables de un MONOSTATE, al ser estáticas, tienen tiempos de creación y destrucción bien definidos.
Costos de MONOSTATO
Sin conversión Una clase normal no se puede convertir en una clase MONOSTATE mediante derivación.
Eficiencia. Un MONOSTATO puede atravesar muchas creaciones y destrucciones porque es un objeto real. Estas operaciones a menudo son costosas.
Presencia. Las variables de un MONOSTATE ocupan espacio, incluso si el MONOSTATE nunca se usa.
Desarrollo ágil de software, principios, patrones y prácticas Robert C. Martin
monostate y singleton son dos caras de la misma medalla (estado global):
- monostate fuerza un comportamiento (solo un valor a lo largo de todas las instancias de clase)
- singleton fuerza una restricción estructural (solo una instancia)
el uso de singleton no es transparente
es decir:
Singleton singleton = Singleton.getInstance();
el uso de monostate es transparente
es decir:
MonoState m1 = new MonoState();
MonoState m2 = new MonoState(); // same state of m1