archivo

Archivo de la etiqueta: herencia

En el artículo anterior vimos una posible forma de enfocar la gestión CRUD en los casos de uso. En este voy a añadir un par de posibles soluciones a circunstancias que se pueden dar en la definición de los casos de uso de un sistema de información:

1) Puede haber alguna tabla maestra que tenga alguna operación expecial como por ejemplo el duplicado de un registro (puede ser útil sobre todo en aquellos casos donde cada registro tengo un número elevado de atributos).

¿Cómo podemos llevar esto al modelo de casos de uso?

Seguiriamos teniendo la entidad donde se realiza esta operación con una relación de herencia sobre el caso de uso “Gestionar Entidad”. Al hacerlo entendemos que se hereda el comportamiento del mismo, así como las relaciones que tenga. Como se trata de una operación más, pues a nivel de diagrama de casos de uso bastará con incluir un nuevo caso de uso en el que se describa la interacción sistema/actores para la realización de la operación que extenderá al caso de uso de la entidad, el cual no será necesario describir ya que en nada varía su funcionamiento.

2) ¿Y si alguna operación tiene un comportamiento especial en la gestión de una entidad?

Una posible solución consiste en que se describa esa operación en un caso de uso y extienda al caso de uso de la entidad (que a su vez tiene una relación de herencia con el caso de uso genérico “Gestionar Entidad”).

Se entenderá que ese caso de uso “sobreescribe” al caso de uso definido para dicha operación en el caso de uso genérico.

3) A veces hay entidades más complejas que comparten parte del funcionamiento de una gestión CRUD (como por ejemplo el alta o la consulta), ¿cómo representarlo?.

Una posible solución sería aplicar una solución similar a la que he expuesto en el caso anterior, todo lo nuevo tendría relaciones extend con respecto al caso de uso de gestión de la entidad (no confundir con el caso de uso genérico).

Quiero dejar claro que estas soluciones o estratégicas no buscan una aplicación purista de UML o de la estrategia de diseños de caso de uso, se trata de una forma de aplicar los mismos con el objeto de no que los casos de uso no representen repetición de comportamientos y así poder invertir el esfuerzo en describir aquellas interacciones actores/sistema que tienen un comportamiento específico y que sí resultan interesantes ser descritas de la manera más exacta posible.

Supongamos que una aplicación gestiona 40 tablas maestras, ¿qué aportan los casos de uso de esas 40 entidades si el comportamiento será el mismo, teniendo en cuenta que lo más probable es que su gestión CRUD haya sido codificada por un generador de código? (variarán los atributos que componen las entidades, tal vez alguna requiera de algún tipo de tratamiento especial, pero serán excepciones).

Soy de la opinión de que en estos casos no hay que ser purista y sí práctico.

Una posible solución es crear un caso de uso genérico (por ejemplo denominado Gestionar Entidad) y que se relacionen con él en modo extend un caso de uso por cada operación CRUD. Después del caso de uso genérico heredarían un caso de uso por cada entidad que tenga gestión CRUD (estos casos de uso solo aparecerían en el diagrama de casos de uso y no se describirían).

NOC es otra de las métricas de Chidamber y Kemerer. Como no podía ser de otra forma, mide la complejidad de una clase desde un punto de vista diferente al del resto de métricas propuestas por estos autores. En este caso su cálculo se basa en contar el número de subclases que directamente heredan de la clase para la cual se calcula esta métrica.

Las subclases están acopladas con la clase de la que heredan, esto quiere decir que cuanto más clases hijas tenga una clase más prudencia hay que tener con cambios relacionados con el comportamiento y especificación de los métodos, ya que podría tener trascendencia sobre sus subclases. Por ese motivo, es necesario ser más exhaustivo en las pruebas de clases con un NOC alto, ya que cualquier problema en las mismas tiene un impacto importante en el programa. Por tanto, se puede deducir de esto que clases con un número de hijos elevado son más complicadas de mantener que otras con un número más bajo.

En el árbol de jerarquía de clases de un determinado programa resulta razonable que las clases situadas en los niveles más alto de la jerarquía tengan un valor de NOC superior a las clases que se encuentren en niveles inferiores, por ese motivo, la existencia de clases con un NOC alto en comparación con otras que se encuentran en niveles superior de la jerarquía, puede ser un indicador de un mal diseño de la clase o una utilización no adecuada de la herencia.

Por otro lado, clases con un NOC alto favorecen su comprensibilidad y la capacidad de implementación de nuevas reutilizaciones, ya que existe un número de clases de ejemplo donde se puede estudiar cómo han aplicado la herencia y como se ha reutilizado la clase.

DIT es otra de las métricas propuestas por Chidamber y Kemerer. Como su propio nombre indica calcula para cada clase su distancia o profundidad respecto a la clase más alta en la jerarquía (en el caso de java, por ejemplo será Object, que es superclase (directa o indirectamente) de todas las clases).

DIT es otra medida de complejidad, ya que conforme se va heredando de clases que heredan a otras, es mayor la “carga” de métodos que puede soportar la clase y si por ejemplo la métrica RFC cuenta los métodos propios de la clase, estos métodos heredados también podrían considerarse como propios y por tanto ser tenidos en cuenta como un factor de complejidad.

Utilizando el mismo razonamiento la herencia podría considerarse como otra forma de acoplamiento, por lo que una modificación en la definición de un método de una clase superior o de su comportamiento pueden tener impacto en clases inferiores de la jerarquía cuando se instancian objetos que hacen uso de dichos métodos. Como es lógico, existen determinadas estrategias de diseño y programación que pueden reducir el nivel de acoplamiento (de la misma manera que malas prácticas pueden incrementarlo).

En resumen, una clase que se encuentra en un nivel bajo en la jerarquía de herencias, tendrá teóricamente un comportamiento más impredecible que otras situadas en niveles más altos.

No se trata de criticar la herencia, ni mucho menos, ya que es uno de los pilares de la programación orientada a objetos y de la reutilización de código (algo fundamental para la reducción del número de errores y por tanto conseguir sistemas de mayor calidad, cuando se hereda de clases suficientemente probadas y documentadas y sobre todo para reducir los costes de desarrollo), sino de intentar explicar por qué una clase de una determinada aplicación que se encuentra en un nivel inferior en el árbol de herencias de la misma puede considerarse de mayor complejidad (desde este punto de vista, porque como he publicado en otros artículos, la complejidad de una clase puede verse desde muy diversas formas y esta es una más) que otra que se encuentra en un nivel superior.

En el caso de java, el hecho de que únicamente se pueda heredar cada vez de una clase (herencia simple) permite reducir la complejidad en el árbol de herencia (cierto es que la profundidad no varía, pero el hecho de que cada nodo pueda tener más de un nodo padre “multiplica” los atributos y métodos potencialmente heredables). Cierto es que en java existe se permite la implementación de más de una interfaz (y de esta forma conseguir una especie de herencia múltiple), pero desde mi punto de vista (supongo que puede haber otras opiniones) el nivel de complejidad que se añade es inferior a si se permitiese herencia múltiple.

Como siempre, surge la pregunta del millón, ¿a partir de qué profundidad podemos considerar este tipo de complejidad como un factor de riesgo para una clase? he estado consultando y parece que está bastante aceptado que a partir de una profundidad mayor o igual a 5 empieza a existir ese riesgo (también hay quienes incrementan ese riesgo a profundidades mayores o iguales a 8).

En resumen, no se puede “castigar” a nadie por intentar reutilizar y la herencia es fundamental para ello, no obstante no debemos olvidar que árboles de herencia de mucha profundidad hacen teóricamente más impredecible el comportamiento y funcionamiento de las clases situadas en niveles más bajos de la jerarquía, así como se incrementa su dependencia respecto de otras.