Red de conocimiento informático - Conocimientos de programación - Describa brevemente cómo el diseño de objetos ayuda a la reutilización del código.

Describa brevemente cómo el diseño de objetos ayuda a la reutilización del código.

Entre los "malos olores a código", la redundancia de código es uno de ellos. La forma más básica de lidiar con la redundancia de código es la reutilización del código, y en la orientación a objetos, la reutilización también es una idea básica.

Cuando el diseño orientado a objetos se generalizó, la "reutilización" alguna vez fue promocionada como una de las principales ventajas de la orientación a objetos. Para lograr la "reutilización", los libros de texto siempre nos dicen que debemos encontrar cosas existentes y modificarlas ligeramente en forma de clases derivadas, o agregar nuevas características para lograr la "reutilización" del código. Entonces, cuando se trata de reutilización de código orientado a objetos, la mayoría de los programadores piensan en la "herencia" orientada a objetos. ¿Es este realmente el caso? Podemos ver un ejemplo a continuación:

Ahora hay un proyecto: modelar diferentes características de animales. Los requisitos del proyecto son los siguientes:

1. Cada animal tiene un número diferente de patas

2. /p>

3. Cada animal se mueve de diferentes maneras (considere caminar y volar);

4. tiempo invertido.

Manejar el cambio de "número de tramos" es muy simple y típico. Consiste en utilizar un miembro de datos para almacenar este valor y establecer los métodos de lectura y asignación del valor (también llamado getter/ en Java. ). Sin embargo, generalmente hay dos formas de lidiar con los métodos de movimiento de animales: la primera es usar un miembro de datos para indicar qué método de movimiento tiene el objeto animal (es decir, cambiar de variable, el tema de cambiar de variable no se discutirá en este artículo). el segundo es Se utilizan dos tipos diferentes de clases Animal (ambas derivadas de la clase base Animal), porque la mayoría de las características de Animal son las mismas, solo los métodos de movimiento son diferentes Para reutilizar el código, la herencia es. se utiliza para manejar diferentes piezas, como se muestra en el siguiente diagrama UML:

Hasta ahora, el diseño no ha presentado ningún problema. Pero ahora las necesidades han cambiado (esto sucede todo el tiempo en el desarrollo de software), y cuando es necesario considerar no sólo la forma en que se mueven los animales, sino también los atributos alimentarios de los animales (carnívoros, herbívoros, omnívoros), el problema se vuelve Se vuelve muy complicado porque hay carnívoros voladores (águilas), carnívoros andantes (leones, tigres), herbívoros voladores (gorriones), herbívoros andantes (vacas), e incluso si hay un omnívoro andante (tortuga), entonces se continúa usando la herencia. Para lograr la reutilización del código en el diseño anterior, obtendrá el siguiente diagrama UML de diseño:

Ahora veo que la cantidad de subclases es explosiva (la situación de los omnívoros no se ha considerado aquí). todavía hay características animales que deben tenerse en cuenta, entonces la relación de herencia será más complicada y el número de subclases será enorme, lo que traerá grandes problemas para el mantenimiento posterior.

Utilizar la herencia para lograr la reutilización del código puede funcionar esta vez, pero no siempre. Especializaciones repetidas como esta pueden hacer que el código sea ininteligible o crear redundancia. Las técnicas de especialización acaban creando jerarquías de herencia demasiado profundas. Lo peor es que la jerarquía de herencia es demasiado profunda, lo que hace que el programa sea difícil de entender (cohesión débil), redundante, difícil de probar y que se combinen múltiples conceptos. No es de extrañar que mucha gente piense que la orientación a objetos está sobrevalorada, sobre todo porque todo se reduce al cumplimiento de los requisitos de "reutilización" de la orientación a objetos.

Entonces, ¿estamos realmente indefensos? Las palabras de los maestros de los "Patrones de diseño" parecen resonar en mis oídos: "Considerar lo que debería ser variable en el diseño", "Encapsular conceptos cambiantes" y, lo más importante, "Priorizar el uso de la agregación de objetos (composición), en lugar de herencia de clases". Las palabras del maestro parecen darnos un buen consejo: buscar cambios en el diseño y encapsularlos en una clase, incluir esta clase en otra clase;

Consideremos el ejemplo anterior. Qué cambia: El número de patas que tiene el animal, la forma en que se mueve, los atributos alimentarios del animal, todo eso cambia.

Una vez encontrado el cambio, el siguiente paso es encapsularlo. Tenemos un buen control del número de patas del animal. Consideremos entonces la forma en que se mueven los animales y los atributos alimentarios de los animales. Puedes probar el siguiente diseño:

Encapsula el modo de acción del animal y los atributos de comida del animal en las dos clases AnimalMovement y AnimalFood respectivamente, y sus cambios se reflejan en varias subclases. No hay relación entre el modo de acción del animal y los atributos de comida del animal (de hecho, no hay relación entre ellos). En la clase Animal, hay referencias a AnimalMovement y AimalFood. Cuando AnimalMovement y AimalFood cambian, no hay relación. la clase Animal.

Ahora volvamos a mirar el atributo del número de patas de los animales. De hecho, esto también es un cambio. Parece que lo hemos tratado de manera especial, pero si lo pensamos detenidamente, lo hemos hecho. También lo encapsulé en Int, esto es solo una variable de datos. Pero en un verdadero lenguaje orientado a objetos, todo es un objeto, e incluso los miembros de datos integrados también son objetos (si reemplazamos int con Integer, puede ser más fácil de entender).

De hecho, la reutilización no es la razón para utilizar métodos orientados a objetos. Reducir los costos de mantenimiento y hacer que el código sea más flexible y fácil de ampliar son consideraciones más importantes. La reutilización es ciertamente posible con las técnicas orientadas a objetos adecuadas, pero no utilizando el objeto directamente y luego derivando nuevas variantes del mismo para su reutilización. El resultado de esto es un código difícil de mantener. Entonces: el verdadero poder de los objetos no proviene de la herencia, sino del comportamiento de encapsulación. Pero a menudo utilizamos erróneamente lo primero e ignoramos lo segundo.

¿Entonces la herencia "defendida" por la orientación a objetos no tiene ningún efecto? De hecho, no, al contrario, la herencia juega un papel importante. Sin herencia, no habría polimorfismo, y la segunda solución de diseño anterior no se puede implementar. La herencia en sí no es una habilidad particularmente difícil, pero es difícil utilizarla bien. Entonces, ¿dónde debería utilizarse la herencia? Escuchemos las enseñanzas del maestro: en el aprendizaje OO, la herencia juega un papel muy importante, pero esto no significa que se pueda abusar de ella en todas partes. Por el contrario, al utilizar la herencia, debe ser lo más conservador posible y utilizarla sólo cuando pueda aportar beneficios evidentes. Al decidir si utilizar composición o herencia, una de las formas más sencillas es preguntarse si la subclase se actualizará a la clase base. Si la transformación es necesaria, entonces la herencia es necesaria. Si no, es hora de ver si no se debe utilizar la herencia.

"Priorizar el uso de agregación (composición) de objetos en lugar de herencia de clases", recuerde siempre esta frase y aplíquela a proyectos reales. Tanto la herencia como la composición le permiten crear nuevas clases basadas en clases existentes. Pero normalmente, la composición reutiliza una clase existente como parte de la implementación subyacente de una nueva clase, mientras que la herencia reutiliza su interfaz. Aunque la programación orientada a objetos a menudo enfatiza la herencia, cuando comienzas a diseñar, generalmente debes considerar primero la composición y usar la herencia solo cuando sea necesario. La síntesis será más flexible.

Materiales de referencia:

"Pensamientos sobre programación Java"

"Análisis de patrones de diseño"

"Refactorización: mejora del "diseño" del código existente

"Patrones de diseño: la base del software reutilizable orientado a objetos"