Todos sabemos lo malos que son los Singleton porque ocultan dependencias y por otras razones .
Pero en un marco, podría haber muchos objetos que deban instanciarse solo una vez y llamarse desde cualquier lugar (logger, db, etc.).
Para resolver este problema, me han dicho que use un "Administrador de objetos" (o un contenedor de servicios como Symfony) que almacena internamente todas las referencias a los servicios (registrador, etc.).
Pero, ¿por qué un proveedor de servicios no es tan malo como un singleton puro?
El proveedor de servicios también oculta las dependencias y simplemente termina la creación de la primera istancia. Así que realmente estoy luchando por entender por qué deberíamos usar un proveedor de servicios en lugar de singletons.
PD. Sé que para no ocultar las dependencias debería usar DI (como dice Misko)
Añadir
Yo agregaría: estos días los singletons no son tan malvados, el creador de PHPUnit lo explicó aquí:
DI + Singleton resuelve el problema:
<?php
class Client {
public function doSomething(Singleton $singleton = NULL){
if ($singleton === NULL) {
$singleton = Singleton::getInstance();
}
// ...
}
}
?>
eso es bastante inteligente, incluso si esto no resuelve todos los problemas.
Aparte de DI y Service Container, ¿hay alguna buena solución aceptable para acceder a estos objetos auxiliares?
fuente
Respuestas:
Service Locator es solo el menor de dos males, por así decirlo. El "menor" se reduce a estas cuatro diferencias ( al menos no puedo pensar en ninguna otra en este momento ):
Principio de responsabilidad única
Service Container no viola el principio de responsabilidad única como lo hace Singleton. Los singleton combinan la creación de objetos y la lógica empresarial, mientras que Service Container es estrictamente responsable de administrar los ciclos de vida de los objetos de su aplicación. En ese sentido, Service Container es mejor.
Acoplamiento
Los singletons generalmente están codificados en su aplicación debido a las llamadas a métodos estáticos, lo que conduce a dependencias estrechamente acopladas y difíciles de simular en su código. El SL, por otro lado, es solo una clase y se puede inyectar. Entonces, si bien todos sus clasificados dependerán de ello, al menos es una dependencia poco acoplada. Entonces, a menos que haya implementado ServiceLocator como un Singleton, eso es algo mejor y también más fácil de probar.
Sin embargo, todas las clases que usan ServiceLocator ahora dependerán del ServiceLocator, que también es una forma de acoplamiento. Esto se puede mitigar mediante el uso de una interfaz para ServiceLocator para que no esté vinculado a una implementación de ServiceLocator concreta, pero sus clases dependerán de la existencia de algún tipo de localizador, mientras que no usar un ServiceLocator aumenta la reutilización drásticamente.
Dependencias ocultas
Sin embargo, el problema de ocultar las dependencias sigue existiendo. Cuando simplemente inyecta el localizador a sus clases consumidoras, no conocerá ninguna dependencia. Pero a diferencia del Singleton, el SL normalmente instanciará todas las dependencias necesarias detrás de escena. Por lo tanto, cuando obtiene un Servicio, no termina como Misko Hevery en el ejemplo de CreditCard , por ejemplo , no tiene que crear una instancia de todas las dependencias de las dependencias a mano.
Obtener las dependencias desde el interior de la instancia también viola la Ley de Demeter , que establece que no debe indagar en los colaboradores. Una instancia solo debe hablar con sus colaboradores inmediatos. Este es un problema tanto con Singleton como con ServiceLocator.
Estado global
El problema del estado global también se mitiga un poco porque cuando crea una instancia de un nuevo localizador de servicios entre pruebas, también se eliminan todas las instancias creadas anteriormente (a menos que haya cometido el error y las haya guardado en atributos estáticos en el SL). Eso no es cierto para ningún estado global en las clases administradas por el SL, por supuesto.
También vea Fowler en Service Locator vs Dependency Injection para una discusión mucho más profunda.
Una nota sobre su actualización y el artículo vinculado de Sebastian Bergmann sobre el código de prueba que usa Singletons : Sebastian, de ninguna manera, sugiere que la solución propuesta hace que el uso de Singleons sea menos problemático. Es solo una forma de hacer código que de otra manera sería imposible probar más comprobable. Pero sigue siendo un código problemático. De hecho, señala explícitamente: "El hecho de que puedas, no significa que debas".
fuente
El patrón del localizador de servicios es un anti-patrón. No resuelve el problema de exponer dependencias (no se puede saber al mirar la definición de una clase cuáles son sus dependencias porque no se están inyectando, sino que se están sacando del localizador de servicios).
Entonces, su pregunta es: ¿por qué son buenos los localizadores de servicios? Mi respuesta es: no lo son.
Evite, evite, evite.
fuente
El contenedor de servicios oculta las dependencias como lo hace el patrón Singleton. Es posible que desee sugerir el uso de contenedores de inyección de dependencia en su lugar, ya que tiene todas las ventajas del contenedor de servicios, pero no tiene (hasta donde yo sé) desventajas que tiene el contenedor de servicios.
Hasta donde yo lo entiendo, la única diferencia entre los dos es que en el contenedor de servicios, el contenedor de servicios es el objeto que se está inyectando (ocultando así las dependencias), cuando usa DIC, el DIC inyecta las dependencias apropiadas para usted. La clase administrada por el DIC es completamente ajena al hecho de que está administrada por un DIC, por lo que tiene menos acoplamiento, dependencias claras y pruebas unitarias felices.
Esta es una buena pregunta en SO que explica la diferencia de ambos: ¿Cuál es la diferencia entre los patrones de Inyección de dependencia y Localizador de servicios?
fuente
Porque puede reemplazar fácilmente objetos en Service Container por
1) herencia (la clase Object Manager se puede heredar y los métodos se pueden anular)
2) cambiar la configuración (en el caso de Symfony)
Y, los Singleton son malos no solo por el alto acoplamiento, sino porque son _ Single _ton. Es una arquitectura incorrecta para casi todo tipo de objetos.
Con DI 'puro' (en constructores) pagará un precio muy alto: todos los objetos deben crearse antes de pasar al constructor. Significará más memoria utilizada y menos rendimiento. Además, no siempre el objeto se puede crear y pasar en el constructor - se puede crear una cadena de dependencias ... Mi inglés no es lo suficientemente bueno para discutir sobre eso completamente, lea sobre ello en la documentación de Symfony.
fuente
Para mí, trato de evitar las constantes globales, singletons por una simple razón, hay casos en los que podría necesitar que se ejecuten las API.
Por ejemplo, tengo front-end y admin. Dentro del administrador, quiero que puedan iniciar sesión como usuario. Considere el código dentro de admin.
Esto puede establecer una nueva conexión de base de datos, un nuevo registrador, etc. para la inicialización de la interfaz y verificar si el usuario realmente existe, si es válido, etc. También usaría los servicios de ubicación y cookies separados adecuados.
Mi idea de singleton es: no puede agregar el mismo objeto dentro del padre dos veces. Por ejemplo
te dejaría con una sola instancia y ambas variables apuntando a ella.
Por último, si desea utilizar el desarrollo orientado a objetos, trabaje con objetos, no con clases.
fuente
$api
var alrededor de su marco? No entendí exactamente lo que quieres decir. Además, si la llamadaadd('Logger')
devuelve la misma instancia, básicamente, tiene un cliente de servicio