Desafio de diseño: objetos inteligentes

Estaba preparando un ejemplo para algo3 cuando me surgió esta cuestión.

Como ya he mencionado en algo3 solemos programar juegos. En general los juegos tienen ciertos personajes que van actuando autonomamente con el paso del tiempo, a estos objetos es a lo que llamo objetos inteligentes. Por poner un ejemplo, tomemos el clásico juego de naves Gálaga, donde las naves enemigas actuan autonomamente con cierta lógica interna.

Cuando llevamos esto a código siguiendo el paradigma orientado a objetos, estas naves solo debieran tener un método público del tipo actuar, el cual seria invocado desde el GameLoop. Dentro de dicho método, cada nave decidiria si moverse en una determinada dirección, o si disparar, o si hacer ambas cosas al mismo tiempo, etc. Con el fin de que dicho método no quede demasiado largo, generalmente extraeremos varios métodos privados: uno con la lógica de movimiento, otro con la lógica de disparo, etc, etc.

Ahora la cuestión es: ¿como escribir pruebas unitarias para esta clase cuando tiene un solo método con tanta lógica? Es claro que es posible escribir pruebas unitarias para estos casos, pero si intentan hacerlo verán que no les quedará código muy feliz, pues el código de arrange de cada prueba podria resultar bastante largo y engorroso de escribir debido a todos los caminos posibles dentro del método bajo prueba. Una opción para evitar esta situación podria ser hacer públicos los métodos privados y asi escribir pruebas por separado para la lógica de movimiento, para la lógica de disparo, etc, etc, pero esto implicaría poner públicos ciertos métodos que naturalmente seria privados, con el solo fin de hacer pruebas. Definitivamente esta opción me hace ruido.

¿Entonces? Tengo una propuesta, pero será parte de otro post, mientras tanto, los invito a lo que piensen.

Continuará….

5 comentarios en “Desafio de diseño: objetos inteligentes

  1. Me pasó algo parecido en un código del trabajo hace poco. Un Validador que era invocado desde una página web y que resolvía una validación que era extremadamente complicada. La validación era simplemente un método público y después tenía algo así como siete métodos privados que me resolvían funcionalidades atómicas que me servía mucho probarlas por separado. ¡Y con Unit Tests!

    Mi primer enfoque fue el de tornar todos esos métodos públicos, hice los Unit Tests y lo pude testear bien… Pero no me quedé conformé. ¡Sentí que había violado el encapsulamiento del Validador!

    Luego volví sobre ese código y me di cuenta de que todo se solucionaba delegando lógica en distintas clases. Hice dos clases Util que me resolvían diferentes cuestiones que el Validador necesitaba. Refactoreando un poco, uno de los Utils terminó tomando la identidad de un CollectionUtil que hacía intersecciones y diferencias entre listas genéricas y algunas que otras operaciones más (que encima podría reutilizar en otras partes). ¡Así tenía sentido que esas operaciones fueran públicas! ¡Y así tenía sentido que cada clase pudiera tener sus propios Unit Tests asociados!

    Creo que esta puede ser una solución a la disyuntiva que planteás. Probablemente la nave tenga demasiada lógica que pueda delegar en otros objetos más bobos que realicen operaciones más atómicas y/o más genéricas.

    Saludos! Muy bueno el blog!

Deja un comentario

Este sitio utiliza Akismet para reducir el spam. Conoce cómo se procesan los datos de tus comentarios.