Al examinar la jerarquía de herencia entre las clases Person y Applicant, se identificó un riesgo latente de violación al Principio de Sustitución de Liskov (LSP). El diseño original carecía de un mecanismo centralizado y restrictivo para asegurar que las reglas de negocio e invariantes de los atributos heredados (como el formato del nombre, límites de edad o sexo) se cumplan de manera uniforme en todo el sistema.
La ausencia de un contrato estricto en la clase base genera las siguientes deficiencias:
Inconsistencia de Datos: Las subclases o componentes externos podrían asignar valores inválidos a los atributos heredados si no existen restricciones explícitas en los puntos de acceso.
Ruptura del Comportamiento Esperado: Si una subclase altera o elude de forma indirecta las restricciones que el sistema asume para una entidad Person, los módulos dependientes fallarán al interactuar con instancias de Applicant, rompiendo la sustituibilidad de la jerarquía.
Solución Propuesta
La solución consiste en definir validaciones explícitas dentro de cada método setter de la clase Person, estableciendo un contrato claro y estable que toda subclase debe respetar por defecto.
Como se ilustra en el diagrama UML adjunto, donde Applicant hereda de Person y mantiene una relación de asociación hacia la misma para el atributo partner, la refactorización introduce la siguiente lógica defensiva:
Contrato en la Clase Base (Person): Cada setter (setName, setAge, setSex) encapsula las reglas de negocio e integridad requeridas, lanzando excepciones o rechazando valores que corrompan el estado del objeto.
Extensión Segura en la Subclase (Applicant): La clase Applicant extiende de forma limpia este contrato agregando exclusivamente sus propias reglas de negocio (como la validación del applicantId o las restricciones del partner) sin sobreescribir ni contradecir las reglas heredadas de la clase madre.
Al asegurar que Applicant preserva intactas las precondiciones y postcondiciones de la clase base, se logra que ambas clases sean completamente intercambiables de forma segura dentro de cualquier flujo de la aplicación.
Beneficios de esta Propuesta
Cumplimiento Estricto de LSP: Garantiza que cualquier módulo que espere un objeto de tipo Person pueda recibir un Applicant y funcionar de manera idéntica y predictiva sin lanzar errores inesperados.
Encapsulamiento Sólido: Al centralizar las validaciones directamente en los métodos modificadores (setters), se evita la duplicación de lógica de control en controladores o capas externas de la interfaz de usuario.
Robusto ante la Extensión: Si en el futuro se agregan nuevas subclases de Person (por ejemplo, Employee o Client), heredarán automáticamente las mismas protecciones, previniendo la corrupción de datos.
Mantenibilidad: Simplifica la depuración del código, ya que las fallas de asignación de datos se detectan inmediatamente en el punto de origen de la mutación del estado.
Al examinar la jerarquía de herencia entre las clases Person y Applicant, se identificó un riesgo latente de violación al Principio de Sustitución de Liskov (LSP). El diseño original carecía de un mecanismo centralizado y restrictivo para asegurar que las reglas de negocio e invariantes de los atributos heredados (como el formato del nombre, límites de edad o sexo) se cumplan de manera uniforme en todo el sistema.
La ausencia de un contrato estricto en la clase base genera las siguientes deficiencias:
Inconsistencia de Datos: Las subclases o componentes externos podrían asignar valores inválidos a los atributos heredados si no existen restricciones explícitas en los puntos de acceso.
Ruptura del Comportamiento Esperado: Si una subclase altera o elude de forma indirecta las restricciones que el sistema asume para una entidad Person, los módulos dependientes fallarán al interactuar con instancias de Applicant, rompiendo la sustituibilidad de la jerarquía.
Solución Propuesta
La solución consiste en definir validaciones explícitas dentro de cada método setter de la clase Person, estableciendo un contrato claro y estable que toda subclase debe respetar por defecto.
Como se ilustra en el diagrama UML adjunto, donde Applicant hereda de Person y mantiene una relación de asociación hacia la misma para el atributo partner, la refactorización introduce la siguiente lógica defensiva:
Contrato en la Clase Base (Person): Cada setter (setName, setAge, setSex) encapsula las reglas de negocio e integridad requeridas, lanzando excepciones o rechazando valores que corrompan el estado del objeto.
Extensión Segura en la Subclase (Applicant): La clase Applicant extiende de forma limpia este contrato agregando exclusivamente sus propias reglas de negocio (como la validación del applicantId o las restricciones del partner) sin sobreescribir ni contradecir las reglas heredadas de la clase madre.
Al asegurar que Applicant preserva intactas las precondiciones y postcondiciones de la clase base, se logra que ambas clases sean completamente intercambiables de forma segura dentro de cualquier flujo de la aplicación.
Beneficios de esta Propuesta
Cumplimiento Estricto de LSP: Garantiza que cualquier módulo que espere un objeto de tipo Person pueda recibir un Applicant y funcionar de manera idéntica y predictiva sin lanzar errores inesperados.
Encapsulamiento Sólido: Al centralizar las validaciones directamente en los métodos modificadores (setters), se evita la duplicación de lógica de control en controladores o capas externas de la interfaz de usuario.
Robusto ante la Extensión: Si en el futuro se agregan nuevas subclases de Person (por ejemplo, Employee o Client), heredarán automáticamente las mismas protecciones, previniendo la corrupción de datos.
Mantenibilidad: Simplifica la depuración del código, ya que las fallas de asignación de datos se detectan inmediatamente en el punto de origen de la mutación del estado.