Quiero distinguir entre dos formas diferentes de abordar la programación orientada a objetos
- Simulacionista: sus objetos representan objetos de dominio real, los ha programado para manejar cualquier funcionalidad relacionada con ese dominio. Es probable que los objetos programados de esta manera tengan muchos estados mutables y colaboradores ocultos para implementar esta funcionalidad.
- Registros + funciones: sus objetos son solo paquetes de datos y funciones que operan sobre esos datos. Los objetos programados de esta manera son más aptos para ser inmutables, asumir menos responsabilidades y permitir inyectar colaboradores.
Una regla general es que un objeto programado en la primera forma tendrá más métodos y más voidmétodos que en la segunda forma. Digamos que íbamos a escribir un simulador de vuelo y estábamos diseñando una clase de avión. Tendríamos algo como:
class Plane {
void accelerate();
void deccelerate();
void toggleRightFlaps();
void toggleLeftFlaps();
void turnRudderRight();
void turnRudderLeft();
void deployLandingGear();
void liftLandingGear();
// etc.
void tick() throws PlaneCrashedException;
}
Esto es quizás un poco más extremo de lo que uno podría encontrar, pero hace que se entienda. Si desea implementar este tipo de interfaz, debe mantenerse dentro del objeto:
- Toda la información sobre el estado del equipamiento del avión.
- Toda la información sobre la velocidad / aceleración del avión.
- La frecuencia de actualización de nuestra simulación (para implementar tick).
- Detalles completos sobre el modelo 3D de la simulación y su física para implementar la marca.
Escribir una prueba unitaria para un objeto escrito en el modo es muy difícil porque:
- Debe proporcionar todos los diferentes bits de datos y colaboradores que este objeto necesita al comienzo de su prueba (instaurarlos puede ser realmente tedioso).
- Cuando desea probar un método, se encuentra con dos problemas: a) la interfaz con frecuencia no expone suficientes datos para que los pruebe (por lo que termina teniendo que usar simulacros / reflexión para verificar incluso las expectativas) b) hay muchos componentes vinculados en uno que debe verificar se comportó en cada prueba.
Básicamente, comienzas con una interfaz que parece razonable y que se adapta bien al dominio, pero la simulación de la simulación te ha engañado para crear un objeto que es realmente difícil de probar.
Sin embargo, puede crear objetos que cumplirían el mismo propósito. Querrías Planedividirlo en pedazos más pequeños. Tenga un PlaneParticledispositivo que rastree los bits físicos del avión, la posición, la velocidad, la aceleración, el balanceo, la guiñada, etc., etc., exponiendo y permitiendo que uno los manipule. Entonces un PlanePartsobjeto podría rastrear el estado de. Se podría enviar tick()a un lugar completamente diferente, por ejemplo, tienen un PlanePhysicsobjeto parametrizado por, por ejemplo, la fuerza de la gravedad, que sabe dar una PlaneParticley una PlanePartsforma de escupir una nueva PlaneParticle. Todo esto podría ser completamente inmutable, aunque no es necesario que sea por algún ejemplo.
Ahora tiene estas ventajas en términos de pruebas:
- Cada componente individual tiene menos que hacer y es más fácil de configurar.
- Puede probar sus componentes de forma aislada.
- Estos objetos pueden salirse al exponer sus elementos internos (especialmente si se hacen inmutables), por lo que no se necesita inteligencia para medirlos.
Aquí está el truco: el segundo enfoque orientado a objetos que describí está muy cerca de la programación funcional. Tal vez en los programas funcionales puros sus registros y sus funciones estén separados y no unidos en objetos, definitivamente un programa funcional garantizaría que todas estas cosas. Lo que creo que realmente facilita las pruebas unitarias es
- Unidades pequeñas (pequeño espacio de estado).
- Funciones con entrada mínima (sin entradas ocultas).
- Funciones con salida mínima.
La programación funcional fomenta estas cosas (pero uno puede escribir programas malos en cualquier paradigma), pero se pueden lograr en programas orientados a objetos. Y destacaría que la programación funcional y la programación orientada a objetos no son incompatibles.