2012-03-13 15 views
5

En general, ¿la creación de instancias de clases con métodos, pero sin campos ni propiedades, tiene demasiados gastos generales?¿Qué tan rápido es la creación de instancias de clase con métodos, pero no campos ni propiedades?

Estoy desarrollando una aplicación ASP.NET MVC que usa mucho la inyección de constructor y algunos controladores tienen hasta 10 dependencias hasta ahora. Sin embargo, debido al alto número de dependencias, recurrí a una interfaz IMyAppServiceProvider y clase que proporciona acceso genérico a todas las dependencias, a través de la DependencyResolver en MVC 3.

Arranqué todo mi código específico de la aplicación y creé un Gist con mi configuración básica (sin embargo, esto no incluye la configuración de BaseController mencionada a continuación).

También creé una clase BaseController que acepta el IMyAppServiceProvider. Todos los controladores heredan de esta clase base. La clase base toma el objeto IMyAppServiceProvider y tiene variables protegidas para todos los servicios. El código se ve más o menos así:

public class BaseController 
{ 
    protected IService1 _service1; 
    protected IService2 _service2; 
    protected IService3 _service3; 
    // ... 

    public BaseController(IMyAppServiceProvider serviceProvider) 
    { 
     _service1 = serviceProvider.GetService<IService1>; 
     _service2 = serviceProvider.GetService<IService2>; 
     _service3 = serviceProvider.GetService<IService3>; 
     // ... 
    } 
} 

Esto hace que el código para los controladores "muy limpio". Ninguna variable privada/protegida, ninguna asignación en el constructor y los servicios son referenciados por las variables protegidas de la clase base. Sin embargo, cada solicitud creará una instancia de cada uno de los servicios que utiliza mi aplicación, independientemente de si el controlador específico los usa o no.

Mis servicios son simples y solo contienen llamadas a métodos con alguna lógica comercial e interacción con la base de datos. Son apátridas y no tienen campos de clase o propiedades. Por lo tanto, la instanciación debe ser rápida, pero me pregunto si esta es una mejor práctica (sé que es un término cargado).

+2

Como siempre, primero en el perfil. Sin embargo, voy a adivinar que la resolución de las dependencias es de un orden de magnitud más lenta que la creación de instancias. –

+0

@insta Wow, no vi el bosque a través de los árboles. Pero, ¿se resuelven los problemas de la memoria caché de Ninject u otros contenedores de IoC? –

+1

Agregué una respuesta a continuación que debería ayudarte ... Me he encontrado con este mismo problema exactamente antes. La mayoría de las personas (incluido yo mismo) no saben que se puede anular la fábrica de controladores que ASP.NET MVC utiliza para crear controladores. Una personalizada puede integrar su contenedor de IoC con fuerza y ​​distribuir dependencias a medida que se necesiten. –

Respuesta

7

cada petición será una instancia de cada servicio que mi solicitud utiliza, ya sea o no el controlador específico utiliza todos ellos.

Creo que ha respondido a su pregunta usted mismo, este no es un buen enfoque. Además, el uso de este tipo de resolución de dependencia (inyección de Localizador de Servicio) es una mala práctica ya que la API del Controlador se vuelve desordenada. Los clientes del controlador no conocen qué servicios son realmente necesarios para un controlador específico, por lo que puede terminar con errores inesperados de tiempo de ejecución, las pruebas unitarias también serán un desastre.

BTW una sugerencia más: marque todas las clases que se consideran una clase base por palabra clave abstract, de esta manera puede evitar usarlo como una clase concreta. Diseñar e implementar una clase base es una decisión de diseño concreta, por lo tanto, aclare sus intenciones de diseño.

En cuanto a un costo de ejemplificación, en su caso, no haría una gran diferencia, pero, en general, reducir el coste de los objetos pesados ​​instanciación puede:

  • Uso Prototype pattern a "evitar el costo inherente de crear un nuevo objeto de la manera estándar (por ejemplo, usando la palabra clave 'nueva') cuando es prohibitivamente costoso para una aplicación determinada " (c) Wikipedia
  • Use Lazy Initialization para servicios que no son necesarios desde el inicio del tiempo de duración del objeto propietario, por lo que se inicializarían a demanda. Desde .NET Framework 4.0 usted puede usar construido en Lazy(T) class
+0

Buena respuesta, pero me gustaría defenderme un poco. Mi API de controlador ahora es MUY limpia porque resumí todas las dependencias en el localizador de servicios. Podría hacer una interfaz más específica para cada controlador, pero para mi aplicación/carga particular, es excesivo. Además, no sufriré errores de tiempo de ejecución, porque las pruebas de mi unidad garantizan que el código se ejecute y se ejecute correctamente. Pero veo tu punto acerca de la falta de conocimiento de las dependencias. Ahora que he hecho esto, no tengo una forma fácil de conocer las dependencias de un Controlador (para mi propia referencia). Estoy bien con eso, por ahora. –

+0

Ok, de todos modos, esto es bueno, mantén ese punto en mente – sll

2

Creo que la solución que está buscando aquí es utilizar una fábrica de controlador personalizado. De esta forma, cada controlador creado tiene exactamente las dependencias que necesita. Aquí hay uno para StructureMap, de weblogs.asp.net:

using StructureMap; 
public class StructureMapControllerFactory : DefaultControllerFactory { 

    protected override IController GetControllerInstance(Type controllerType) { 
     try { 
      return ObjectFactory.GetInstance(controllerType) as Controller; 
     } 
     catch (StructureMapException) { 
      System.Diagnostics.Debug.WriteLine(ObjectFactory.WhatDoIHave()); 
      throw; 
     } 
    } 
} 

protected void Application_Start() { 
    RegisterRoutes(RouteTable.Routes); 

    //Configure StructureMapConfiguration 
    // TODO: config structuremap   

    //Set current Controller factory as StructureMapControllerFactory 
    ControllerBuilder.Current.SetControllerFactory(new StructureMapControllerFactory()); 
} 
+0

Gracias por la respuesta, pero creo que ya tengo esto? Anteriormente tenía la configuración de constructores del controlador para aceptar interfaces para sus dependencias de servicio. Ninject y Bootstrapper para lograr esto. –

+0

Entonces, ¿cuál es el punto de un controlador base que toma cada dependencia? Si sus controladores han sido capaces de tomar solo los argumentos del constructor que necesitan, sin embargo están llenos, entonces eso es lo mejor que puede hacer. –

+0

Entonces, en lugar de tener 10 campos privados, 10 argumentos de constructor y 10 asignaciones en el cuerpo del constructor, lo manejo una vez en el BaseController. De esa manera, cuando agregue una nueva dependencia, los constructores del controlador no cambiarán. En cambio, el BaseController obtendrá un nuevo campo y asignación en su constructor. Del mismo modo, esto simplifica mi configuración de prueba también. Tengo una clase BaseTests que configura campos para cada simulacro, en un solo lugar. –

Cuestiones relacionadas