Programación de métodos abstractos
Las clases e interfaces abstractas se utilizan para definir clases abstractas en el lenguaje Java (la clase abstracta en este artículo no se traduce de la clase abstracta, representa un cuerpo abstracto y la clase abstracta es el lenguaje Java Métodos utilizados para definir clases abstractas, preste atención a la distinción). Entonces, ¿qué es una clase abstracta y qué beneficios puede aportarnos?
En el concepto de orientación a objetos, sabemos que todos los objetos se describen mediante clases, pero lo contrario no es cierto. No todas las clases se utilizan para describir objetos. Si una clase no contiene suficiente información para describir un objeto específico, dicha clase es una clase abstracta. Las clases abstractas se suelen utilizar para representar los conceptos abstractos que obtenemos del análisis y diseño del dominio del problema. Son abstracciones de un conjunto de conceptos concretos que parecen diferentes pero que son esencialmente iguales. Por ejemplo, cuando desarrollamos un software de edición de gráficos, encontraremos que existen algunos conceptos específicos como círculos y triángulos en el dominio del problema. Son diferentes, pero todos pertenecen al concepto de forma. El concepto de forma no existe en el dominio del problema, pero es un concepto abstracto. Precisamente porque los conceptos abstractos no tienen conceptos concretos correspondientes en el dominio del problema, no se pueden crear instancias de las clases abstractas utilizadas para representar conceptos abstractos.
En el campo orientado a objetos, las clases abstractas se utilizan principalmente para ocultar tipos. Podemos construir una descripción abstracta de un conjunto fijo de comportamientos, pero este conjunto de comportamientos puede tener cualquier implementación concreta posible. Esta descripción abstracta es una clase abstracta y cualquier posible implementación concreta de este grupo está representada por todas las clases derivadas posibles. Los módulos pueden operar con abstracciones. Debido a que un módulo se basa en una abstracción fija, es posible que no permita modificaciones al mismo tiempo; el comportamiento de este módulo también se puede ampliar derivando de esta abstracción; Los lectores que estén familiarizados con OCP deben saber que las clases abstractas son la clave para realizar el principio central del diseño orientado a objetos OCP (principio de apertura y cierre).
Observando clases e interfaces abstractas desde la perspectiva de la definición gramatical
A nivel gramatical, el lenguaje Java proporciona diferentes definiciones de clases e interfaces abstractas. Tomemos como ejemplo la definición de una clase abstracta denominada Demo para ilustrar esta diferencia.
El método para usar una clase abstracta para definir y demostrar una clase abstracta es el siguiente:
Demostración de clase abstracta{
Método vacío abstracto 1();
Método abstracto void 2();
<…
}
El método para usar la definición de interfaz para demostrar una clase abstracta es el siguiente :
Demostración de interfaz {
método vacío 1();
método vacío 2(); p>}
En el modo de clase abstracta, Demo puede tener sus propios miembros de datos o métodos de miembros no abstractos, mientras que en la implementación del modo de interfaz, Demo solo puede tener miembros de datos estáticos no modificables (es decir, debe ser estático final, pero generalmente no está definido en la interfaz (miembros de datos), todos los métodos miembros son abstractos. En cierto sentido, una interfaz es una forma especial de clase abstracta.
Más detalles sobre la definición gramatical de clases e interfaces abstractas no son el tema central de este artículo y no se repetirán aquí. Para obtener más información relacionada, los lectores pueden consultar la referencia [1].
Observando clases e interfaces abstractas desde una perspectiva de programación
Desde una perspectiva de programación, tanto las clases como las interfaces abstractas se pueden utilizar para implementar la idea de "diseño contractual". Sin embargo, todavía existen algunas diferencias en el uso específico.
En primer lugar, las clases abstractas representan una relación de herencia en el lenguaje Java, y una clase solo puede usar la relación de herencia una vez. Sin embargo, una clase puede implementar múltiples interfaces. Quizás este sea un compromiso hecho por los diseñadores del lenguaje Java al considerar el soporte de Java para la herencia múltiple.
En segundo lugar, en la definición de clase abstracta, podemos darle al método un comportamiento predeterminado. Sin embargo, en la definición de una interfaz, los métodos no pueden tener un comportamiento predeterminado. Para sortear esta limitación, es necesario utilizar delegados, pero esto añade cierta complejidad y, en ocasiones, puede causar muchos problemas.
También existe un problema grave: el comportamiento predeterminado no se puede definir en clases abstractas, lo que puede causar problemas de mantenimiento. Porque si desea modificar la interfaz de una clase (generalmente representada por una clase o interfaz abstracta) en el futuro para adaptarse a nuevas situaciones (como agregar nuevos métodos o agregar nuevos parámetros a los métodos usados), será muy problemático y puede costar mucho tiempo (especialmente en el caso de muchas clases derivadas).
Sin embargo, si la interfaz se implementa a través de una clase abstracta, es posible que solo necesite modificar el comportamiento predeterminado definido en la clase abstracta.
De manera similar, si el comportamiento predeterminado no se puede definir en la clase abstracta, la misma implementación del método aparecerá en cada clase derivada de la clase abstracta, lo que viola el principio de "una regla, un lugar" y provoca que el código duplicación. , lo que tampoco favorece el mantenimiento futuro. Por lo tanto, tenga mucho cuidado al elegir entre clases e interfaces abstractas.
Observación de clases e interfaces abstractas desde la perspectiva de los conceptos de diseño
La diferencia entre clases e interfaces abstractas se analiza principalmente desde la perspectiva de la definición de sintaxis y la programación. Las diferencias en estos niveles son relativamente bajas y no esenciales. Esta sección analizará la diferencia entre clases abstractas e interfaces desde otro aspecto: los conceptos de diseño que reflejan. El autor cree que sólo analizando desde este nivel podemos comprender la esencia de estos dos conceptos.
Como se mencionó anteriormente, la clase abstarct encarna la relación de herencia en el lenguaje Java. Para que la relación de herencia sea razonable, debe haber una relación "es un" entre la clase principal y la clase derivada, es decir, la clase principal y la clase derivada deben ser conceptualmente iguales (la referencia [3] tiene un " es una" relación Los lectores interesados pueden consultar la extensa discusión en profundidad). Este no es el caso de las interfaces. No es necesario que el implementador de la interfaz sea conceptualmente coherente con la definición de la interfaz, solo implementa el contrato definido por la interfaz. Para que la discusión sea fácil de entender, a continuación se explicará un ejemplo sencillo.
Considere este ejemplo. Supongamos que hay un concepto abstracto sobre una puerta en nuestro dominio del problema. La puerta tiene dos acciones, abrirse y cerrarse. En este momento, podemos definir un tipo que represente un concepto abstracto a través de una clase o interfaz abstracta. El método de definición es el siguiente:
Utilice clases abstractas para definir puertas:
Resumen. puerta de clase{
Abstract void open();
Abstract void close();
}
Usa la interfaz para definir puertas:
Puerta de interfaz {
void open();
void close()
}
Otro. Se pueden ampliar tipos de puertas específicos. Definidos por una clase abstracta o una interfaz en su implementación. No parece haber mucha diferencia entre usar clases e interfaces abstractas.
Si ahora se necesita una puerta, también debería tener función de alarma. ¿Cómo podemos diseñar la estructura de clases para este ejemplo (en este ejemplo, es principalmente para reflejar la diferencia entre clases abstractas e interfaces en el concepto de diseño, y otros problemas irrelevantes se simplifican o ignoran)? Las posibles soluciones se enumeran a continuación y estas diferentes soluciones se analizan a nivel de concepto de diseño.
Solución 1:
Simplemente agregue un método de alarma a la definición de puerta de la siguiente manera:
Puerta de clase abstracta{
Vacío abstracto abierto ();
Cierre de vacío abstracto();
Alarma de vacío abstracto();
}
O
Puerta de interfaz {
void open();
void close()
void alarm();
}
Entonces la puerta de alarma con función de alarma se define de la siguiente manera:
clase Puerta de expansión AlarmDoor {
void open() { … }
void close() { … }
Alarma de anulación(){ … }
}
o
Nivel de puerta de alarma Puerta de herramientas {
void open() { … }
void close() { … }
Alarma no válida(){ … }
}
Este enfoque viola uno de los principios básicos del diseño orientado a objetos, ISP (Interface Isolation Pripler). En la definición de puerta se mezcla el comportamiento inherente al concepto de puerta con el comportamiento de otro concepto, “alarma”. Un problema que surge de esto es que aquellos módulos que sólo se basan en el concepto de puerta cambiarán debido a cambios en el concepto de "alarma" (como la modificación de los parámetros del método de alarma), y viceversa.
Solución 2:
Dado que apertura, cierre y alarma pertenecen a dos conceptos diferentes, se deben definir en clases abstractas que representen estos dos conceptos según el principio ISP. La definición es la siguiente: ambos conceptos están definidos por clases abstractas; ambos conceptos están definidos por interfaces; un concepto está definido por una clase abstracta y el otro concepto está definido por una interfaz.
Obviamente, debido a que el lenguaje Java no admite herencia múltiple, no es factible definir estos dos conceptos en forma de clases abstractas. Los dos últimos métodos son factibles, pero su elección refleja una comprensión de la naturaleza de los conceptos en el dominio del problema y si el reflejo de la intención del diseño es correcto y razonable. Analicémoslo y expliquemos.
Si ambos conceptos están definidos por interfaces, entonces refleja dos problemas: 1. Es posible que no comprendamos claramente el dominio del problema. ¿AlarmDoor es esencialmente una puerta o una alarma? 2. Si no hay ningún problema con nuestra comprensión del dominio del problema, por ejemplo, mediante el análisis del dominio del problema, encontramos que el concepto de AlarmDoor y el concepto de Door son esencialmente iguales, entonces no podemos revelar correctamente nuestro diseño cuando implementarlo Intención, porque el significado anterior no se puede reflejar en la definición de estos dos conceptos (ambos se definen a través de interfaces).
Si nuestra comprensión del área del problema es: AlarmDoor es esencialmente una puerta, que tiene la función de alarmar. ¿Cómo lo diseñamos e implementamos para reflejar claramente lo que queremos decir? Como se mencionó anteriormente, las clases abstractas representan una relación de herencia en el lenguaje Java, que es esencialmente una relación "es". Por lo tanto, para el concepto de puerta, debemos utilizar el método de clase abstracta para definirlo. Además, AlarmDoor tiene una función de alarma, lo que significa que puede completar el comportamiento definido en el concepto de alarma, por lo que el concepto de alarma se puede definir a través de la interfaz. Como se muestra a continuación:
Puerta de clase abstracta{
Abstract void open();
Abstract void close();
}
Alarma de interfaz{
alarma nula();
}
Alarma de herramienta de puerta de extensión de puerta de nivel de alarma{
void open() { … }
void close() { … }
void alert() { … }
}
Este método de implementación básicamente puede reflejar claramente nuestra comprensión del dominio del problema y revelar correctamente nuestras intenciones de diseño. De hecho, las clases abstractas representan la relación "es un", y las interfaces representan la relación "como un", que puede usarse como base para la elección de todos. Por supuesto, esto se basa en la comprensión del dominio del problema. Por ejemplo, si pensamos que AlarmDoor es esencialmente una alarma y también tiene la función de una puerta, entonces la definición anterior se invierte.
Conclusión
Las clases e interfaces abstractas son dos formas de definir clases abstractas en el lenguaje Java y son muy similares. Pero su selección a menudo refleja la comprensión de la naturaleza de los conceptos en el dominio del problema y si el reflejo de la intención del diseño es correcto y razonable, porque muestran la relación entre diferentes conceptos (aunque todos pueden lograr las funciones requeridas). En realidad, este es un nuevo uso del lenguaje. Espero que los lectores puedan comprenderlo con atención.