Recientemente leí el artículo de Mark Seemann sobre el antipatrón del Localizador de servicios.
El autor señala dos razones principales por las que ServiceLocator es un antipatrón:
Problema de uso de la API (con el que estoy perfectamente de acuerdo)
Cuando la clase emplea un localizador de servicios, es muy difícil ver sus dependencias ya que, en la mayoría de los casos, la clase solo tiene un constructor PARAMETERLESS. A diferencia de ServiceLocator, el enfoque DI expone explícitamente las dependencias a través de los parámetros del constructor para que las dependencias se vean fácilmente en IntelliSense.Problema de mantenimiento (que me desconcierta)
Considere el siguiente ejemplo
Tenemos una clase 'MyType' que emplea un enfoque de localización de servicios:
public class MyType
{
public void MyMethod()
{
var dep1 = Locator.Resolve<IDep1>();
dep1.DoSomething();
}
}
Ahora queremos agregar otra dependencia a la clase 'MyType'
public class MyType
{
public void MyMethod()
{
var dep1 = Locator.Resolve<IDep1>();
dep1.DoSomething();
// new dependency
var dep2 = Locator.Resolve<IDep2>();
dep2.DoSomething();
}
}
Y aquí es donde comienza mi malentendido. El autor dice:
Se vuelve mucho más difícil saber si está introduciendo un cambio importante o no. Debe comprender toda la aplicación en la que se utiliza el Localizador de servicios, y el compilador no lo ayudará.
Pero espere un segundo, si estuviéramos usando el enfoque DI, introduciríamos una dependencia con otro parámetro en el constructor (en caso de inyección del constructor). Y el problema seguirá ahí. Si nos olvidamos de configurar ServiceLocator, entonces podemos olvidar agregar una nueva asignación en nuestro contenedor IoC y el enfoque DI tendría el mismo problema de tiempo de ejecución.
Además, el autor mencionó las dificultades de las pruebas unitarias. Pero, ¿no tendremos problemas con el enfoque DI? ¿No necesitaremos actualizar todas las pruebas que instanciaron esa clase? Los actualizaremos para pasar una nueva dependencia simulada solo para que nuestra prueba sea compilable. Y no veo ningún beneficio de esa actualización y gasto de tiempo.
No estoy tratando de defender el enfoque del Localizador de servicios. Pero este malentendido me hace pensar que estoy perdiendo algo muy importante. ¿Alguien podría disipar mis dudas?
ACTUALIZACIÓN (RESUMEN):
La respuesta a mi pregunta "¿Es el localizador de servicios un antipatrón" realmente depende de las circunstancias. Y definitivamente no sugeriría tacharlo de su lista de herramientas. Puede ser muy útil cuando comienzas a lidiar con código heredado. Si tiene la suerte de estar al comienzo de su proyecto, entonces el enfoque DI podría ser una mejor opción ya que tiene algunas ventajas sobre el Localizador de servicios.
Y aquí están las principales diferencias que me convencieron de no usar Service Locator para mis nuevos proyectos:
- Lo más obvio e importante: Service Locator oculta las dependencias de clase
- Si está utilizando algún contenedor de IoC, es probable que escanee todo el constructor al inicio para validar todas las dependencias y le dará comentarios inmediatos sobre las asignaciones faltantes (o la configuración incorrecta); Esto no es posible si está utilizando su contenedor IoC como Localizador de servicios
Para más detalles lea las excelentes respuestas que se dan a continuación.
Respuestas:
Si define patrones como antipatrones solo porque hay algunas situaciones en las que no encaja, entonces SÍ es un antipatrón. Pero con ese razonamiento todos los patrones también serían antipatrones.
En cambio, tenemos que ver si hay usos válidos de los patrones, y para Service Locator hay varios casos de uso. Pero comencemos mirando los ejemplos que ha dado.
La pesadilla de mantenimiento con esa clase es que las dependencias están ocultas. Si crea y usa esa clase:
No comprende que tiene dependencias si están ocultas mediante la ubicación del servicio. Ahora, si en su lugar usamos inyección de dependencia:
Puede detectar directamente las dependencias y no puede usar las clases antes de satisfacerlas.
En una aplicación de línea de negocio típica, debe evitar el uso de la ubicación del servicio por esa misma razón. Debe ser el patrón a usar cuando no hay otras opciones.
¿Es el patrón un antipatrón?
No.
Por ejemplo, la inversión de los contenedores de control no funcionaría sin la ubicación del servicio. Es cómo resuelven los servicios internamente.
Pero un ejemplo mucho mejor es ASP.NET MVC y WebApi. ¿Qué crees que hace posible la inyección de dependencia en los controladores? Así es, ubicación del servicio.
Tus preguntas
Hay dos problemas más serios:
Con la inyección del constructor usando un contenedor, obtienes eso gratis.
Es verdad. Pero con la inyección de constructor no tiene que escanear toda la clase para descubrir qué dependencias faltan.
Y algunos mejores contenedores también validan todas las dependencias al inicio (escaneando todos los constructores). Entonces, con esos contenedores obtienes el error de tiempo de ejecución directamente, y no en algún momento temporal posterior.
No. Como no tiene una dependencia de un localizador de servicios estático. ¿Has intentado obtener pruebas paralelas que funcionen con dependencias estáticas? No es divertido.
fuente
También me gustaría señalar que SI está refactorizando el código heredado, el patrón del Localizador de servicios no solo no es un antipatrón, sino que también es una necesidad práctica. Nadie jamás va a agitar una varita mágica sobre millones de líneas de código y, de repente, todo ese código estará listo para DI. Entonces, si desea comenzar a introducir DI a una base de código existente, a menudo es el caso de que cambie las cosas para convertirse en servicios DI lentamente, y el código que hace referencia a estos servicios a menudo NO será servicios DI. Por lo tanto, ESOS servicios necesitarán usar el Localizador de servicios para obtener instancias de aquellos servicios que SE HAN convertido para usar DI.
Entonces, al refactorizar grandes aplicaciones heredadas para comenzar a usar conceptos DI, diría que no solo Service Locator NO es un antipatrón, sino que es la única forma de aplicar gradualmente los conceptos DI a la base del código.
fuente
Desde el punto de vista de la prueba, el Localizador de servicios es malo. Vea la explicación de Google Tech Talk de Misko Hevery con ejemplos de código http://youtu.be/RlfLCWKxHJ0 a partir del minuto 8:45. Me gustó su analogía: si necesita $ 25, solicite dinero directamente en lugar de darle su billetera de donde sacará el dinero. También compara Service Locator con un pajar que tiene la aguja que necesita y sabe cómo recuperarla. Las clases que usan Service Locator son difíciles de reutilizar debido a eso.
fuente
Hay 2 razones diferentes por las que usar el localizador de servicios es malo a este respecto.
Claro y simple: una clase con un localizador de servicios es más difícil de reutilizar que una que acepte sus dependencias a través de su constructor.
fuente
Mi conocimiento no es lo suficientemente bueno para juzgar esto, pero en general, creo que si algo tiene un uso en una situación particular, no significa necesariamente que no pueda ser un antipatrón. Especialmente, cuando se trata de bibliotecas de terceros, no tiene control total sobre todos los aspectos y puede terminar usando la mejor solución.
Aquí hay un párrafo del Código adaptativo a través de C # :
fuente
El autor razona que "el compilador no lo ayudará", y es cierto. Cuando diseñe una clase, tendrá que elegir cuidadosamente su interfaz, entre otros objetivos para que sea tan independiente como ... como tenga sentido.
Al hacer que el cliente acepte la referencia a un servicio (a una dependencia) a través de una interfaz explícita, usted
Tienes razón en que DI tiene sus problemas / desventajas, pero las ventajas mencionadas los superan con diferencia ... IMO. Tiene razón, que con DI hay una dependencia introducida en la interfaz (constructor), pero es de esperar que sea la dependencia que necesita y que desea hacer visible y comprobable.
fuente
Sí, el localizador de servicios es un antipatrón que viola la encapsulación y es sólido .
fuente