¿Cuándo debe y no debe usar la palabra clave 'nuevo'?

14

Vi una presentación de Google Tech Talk sobre Pruebas unitarias , realizada por Misko Hevery, y me dijo que evitara utilizando la palabra clave new en el código de lógica de negocios.

Escribí un programa y terminé usando la palabra clave new aquí y allá, pero eran principalmente para crear instancias de objetos que contienen datos (es decir, no tenían ninguna función o método).

Me pregunto si hice algo mal cuando usé la nueva palabra clave para mi programa. ¿Y dónde podemos romper esa 'regla'?

    
pregunta skizeey 24.10.2011 - 20:02

5 respuestas

16

Esto es más una guía que una regla rápida y rápida.

Al usar "nuevo" en su código de producción, está uniendo su clase con sus colaboradores. Si alguien quiere usar algún otro colaborador, por ejemplo, algún tipo de colaborador simulado para pruebas de unidad, no puede, porque el colaborador está creado en su lógica empresarial.

Por supuesto, alguien necesita crear estos nuevos objetos, pero a menudo es mejor dejarlo en uno de dos lugares: un marco de inyección de dependencias como Spring, o bien en cualquier clase que esté creando una instancia de su clase de lógica empresarial, inyectada a través del constructor.

Por supuesto, puedes llevar esto demasiado lejos. Si desea devolver una nueva ArrayList, entonces probablemente esté bien, especialmente si esta va a ser una Lista inmutable.

La pregunta principal que debe hacerse es "¿es la responsabilidad principal de este bit de código para crear objetos de este tipo, o es solo un detalle de la implementación que podría mover razonablemente a otra parte?"

    
respondido por el Bill Michell 24.10.2011 - 20:10
5

El quid de esta pregunta es al final: Me pregunto si hice algo mal cuando usé la nueva palabra clave para mi programa. ¿Y dónde podemos romper esa 'regla'?

Si puede escribir pruebas de unidad efectivas para su código, entonces no hizo nada malo. Si su uso de new hizo difícil o imposible realizar una prueba unitaria de su código, debe volver a evaluar su uso de nuevo. Podría extender este análisis a la interacción con otras clases, pero la capacidad de escribir pruebas de unidad sólidas suele ser un buen proxy.

    
respondido por el ObscureRobot 24.10.2011 - 20:25
5

En resumen, siempre que use "nuevo", está uniendo la clase que contiene este código al objeto que se está creando; Para poder crear una instancia de uno de estos objetos, la clase que realiza la creación de una instancia debe saber acerca de la clase concreta que está siendo instanciada. Por lo tanto, cuando use "nuevo", debe considerar si la clase en la que está colocando la instanciación es un lugar "bueno" para que ese conocimiento resida, y está dispuesto a hacer cambios en esta área si la forma del el objeto que se está instanciando debe cambiar.

El acoplamiento apretado, que es un objeto que tiene conocimiento de otra clase concreta, no siempre debe evitarse; en algún nivel, algo, EN ALGUNA PARTE, tiene que saber cómo crear este objeto, incluso si todo lo demás trata con el objeto al recibir una copia del mismo en otro lugar. Sin embargo, cuando la clase que se crea cambia, cualquier clase que sepa sobre la implementación concreta de esa clase debe actualizarse para tratar correctamente los cambios de esa clase.

La pregunta que siempre debe hacer es: "¿Saber que esta clase sabrá cómo crear esta otra clase se convertirá en una responsabilidad al mantener la aplicación?" Las dos metodologías de diseño principales (SOLID y GRASP) generalmente responden "sí", por razones sutilmente diferentes. Sin embargo, solo son metodologías, y ambas tienen la limitación extrema de no haber sido formuladas en base al conocimiento de su programa único. Como tales, solo pueden errar por el lado de la precaución y suponer que cualquier punto de acoplamiento apretado EVENTUALMENTE le causará un problema relacionado con la realización de cambios en uno o ambos lados de este punto. Debes tomar una decisión final sabiendo tres cosas; la mejor práctica teórica (que consiste en unir todo de manera flexible porque cualquier cosa puede cambiar); el costo de implementar las mejores prácticas teóricas (que pueden incluir varias capas nuevas de abstracción que facilitarán un tipo de cambio y obstaculizarán otro); y la probabilidad real de que el tipo de cambio que está anticipando siempre será necesario.

Algunas pautas generales:

  • Evite el acoplamiento estrecho entre las bibliotecas de código compiladas. La interfaz entre las DLL (o un EXE y sus DLL) es el lugar principal donde el acoplamiento apretado presentará una desventaja. Si realiza un cambio en una clase A en DLL X, y la clase B en el EXE principal conoce la clase A, debe recompilar y liberar ambos binarios. Dentro de un solo binario, el acoplamiento más apretado es generalmente más permisible porque el binario completo debe ser reconstruido para cualquier cambio de todos modos. A veces, tener que reconstruir múltiples binarios es inevitable, pero debe estructurar su código para poder evitarlo siempre que sea posible, especialmente en situaciones donde el ancho de banda es muy importante (como la implementación de aplicaciones móviles; es mucho más barato incluir una nueva DLL en una actualización). que empujar todo el programa).

  • Evite el acoplamiento estrecho entre los "centros lógicos" principales de su programa. Puede pensar que un programa bien estructurado consiste en cortes horizontales y verticales. Los segmentos horizontales pueden ser niveles de aplicación tradicionales, como UI, Controlador, Dominio, DAO, Datos; los segmentos verticales pueden definirse para ventanas o vistas individuales, o para "historias de usuario" individuales (como crear un nuevo registro de algún tipo básico). Al realizar una llamada que se mueve hacia arriba, hacia abajo, hacia la izquierda o hacia la derecha en un sistema bien estructurado, generalmente debe abstraer dicha llamada. Por ejemplo, cuando la validación necesita recuperar datos, no debe tener acceso a la base de datos directamente, sino que debe realizar una llamada a una interfaz para la recuperación de datos, que está respaldada por el objeto real que sabe cómo hacerlo. Cuando algún control de UI necesita realizar una lógica avanzada con otra ventana, debe abstraer el disparo de esta lógica a través de un evento y / o devolución de llamada; no tiene que saber qué se hará como resultado, lo que le permite cambiar lo que se hará sin cambiar el control que lo activa.

  • En cualquier caso, considere qué tan fácil o difícil será realizar un cambio y qué tan probable será dicho cambio. Si un objeto que está creando solo se usa en un solo lugar, y no prevé que cambie, entonces el acoplamiento apretado es generalmente más permisible, e incluso puede ser superior en esta situación para perder el acoplamiento. El acoplamiento suelto requiere abstracción, que es una capa adicional que evita el cambio a los objetos dependientes cuando la implementación de una dependencia debe cambiar. Sin embargo, si la interfaz en sí misma debe cambiar (agregar una nueva llamada de método o agregar un parámetro a una llamada de método existente), entonces la interfaz en realidad aumenta la cantidad de trabajo necesario para realizar el cambio. Tiene que sopesar la posibilidad de que diferentes tipos de cambios afecten el diseño, y será inviable o incluso imposible hacer un sistema que esté cerrado a todos los tipos de cambios.

respondido por el KeithS 25.10.2011 - 00:26
3

Los objetos que solo contienen datos, o DTO (objetos de transferencia de datos) están bien, créalos todo el día. Cuando desee evitar que new esté en clases que realizan acciones, desea que las clases consumidoras se programen en lugar de la interfaz , donde pueden invocar esa lógica y consumirla, pero no ser responsables de ella Creando las instancias de las clases que contienen la lógica. Aquellas clases de ejecución de acciones o que contienen lógica son dependencias. La charla de Misko está orientada a que usted inyecte esas dependencias y permita que otra entidad sea responsable de crearlas realmente.

    
respondido por el Anthony Pegram 24.10.2011 - 20:08
2

Otros ya han mencionado este punto, pero querían citarlo del Código de Limpieza del Tío Bob (Bob Martin) porque podría facilitar la comprensión del concepto:

"Un poderoso mecanismo para separar la construcción del uso es Dependency Injection (DI), la aplicación de Inversion of Control (IoC) a la gestión de dependencias. La inversión de Control se mueve responsabilidades secundarias de un objeto a otros objetos que están dedicados al propósito, por lo tanto, apoyan el Principio de Responsabilidad Única . En el contexto de la administración de dependencias, un objeto no debe asumir la responsabilidad de instanciar las dependencias en sí. debería pasar esta responsabilidad a otro mecanismo "autoritario", invirtiendo así el control. Debido a que la configuración es una preocupación global, este mecanismo autoritario generalmente será la "rutina principal" o un contenedor para fines especiales ".

Creo que es siempre una buena idea seguir esta regla. Otros mencionaron el más importante IMO - > desacopla a su clase de sus dependencias (colaboradores). La segunda razón es que hace que sus clases sean más pequeñas y terser, más fáciles de entender e incluso reutilizar en otros contextos.

    
respondido por el c_maker 24.10.2011 - 20:39

Lea otras preguntas en las etiquetas