Análisis de polimorfismos en Delphi
1 ¿Qué es el polimorfismo? 2
1.1 Concepto 2
1.2 El significado del polimorfismo 2
1.3 ¿Cómo implementar el polimorfismo en Delphi? 2
1.3.1 Herencia 2
1.3.2 Métodos virtuales, métodos dinámicos y métodos abstractos, VMT/DMT, enlace estático y enlace dinámico 2
1.3.3 Sobrecarga y polimorfismo 2
1.4 Discusión sobre especies polimórficas 2
1.4.1 Polimorfismo de dos niveles 2
1.4.2 Polimorfismo inseguro 2
2 Aplicaciones de polimorfismo en VCL2
2.1 Método de construcción y destrucción 2
2.2 tstrings2
2.3 otros (por favor agregue alma) 2
El polimorfismo es el alma de la comprensión de los objetos.
Polimorfismo de palabras clave , herencia, orientado a objetos, VCL, método virtual, anulación
pregunta
El polimorfismo es el alma de los objetos, y comprender el polimorfismo es una de las claves para dominar la tecnología orientada a objetos. Pero, ¿qué es exactamente el polimorfismo? ¿Cuál es el significado del polimorfismo? ¿Cómo lograr el polimorfismo? Puedo entender el concepto de polimorfismo, pero no sé cómo usarlo y cuándo usarlo. Lea este artículo en detalle.
Análisis de expertos
El mundo (objeto, es decir, objeto) es siempre cambiante; Bueno, en el lenguaje informático. La industria del software entera fue la demanda de la tierra. También han aparecido herramientas de desarrollo orientadas, como VC, Delphi y BCB. Diseño orientado (OOD), análisis orientado a objetos (OOA) y base de datos orientada a objetos (OODB)), la tecnología orientada a objetos casi ha penetrado todo el campo de software, ¡y la forma de pensar de los programadores también ha sufrido cambios fundamentales! A los ojos de algunos teóricos de purificación de OO, ¡todo es un objeto! Aunque no estoy muy de acuerdo con este punto de vista. Pero creo que este método está más en línea con los hábitos de pensamiento de las personas. ¡Cerebro a partir de entonces, me liberaron! ¡Esta es una revolución!
El contenido central de los objetos es objeto, encapsulación, herencia, polimorfismo y mecanismos de mensajes. Polimorfismo maestro, realmente no dominará la tecnología orientada a objetos.
1 ¿Qué es el polimorfismo?
1.1 concepto
Hay muchas opiniones diferentes sobre el concepto de polimorfismo, y las siguientes son varias declaraciones representativas:
"Esta capacidad de manipular más de un tipo con un puntero o una referencia a una clase base habla como polimorfismo" (Primer C ++, página 838). Es decir, la capacidad de operar objetos de múltiples clases (clase base y sus clases derivadas) utilizando punteros/referencias a clases base se llama polimorfismo. Se considera desde la perspectiva de la implementación del lenguaje.
"El polimorfismo proporciona otra dimensión de la separación de la interfaz de la implementación, para desacoplarse de cómo" ("Piense en Java" 3ª edición), es decir, el polimorfismo proporciona otro tipo de interfaz e implementación de separación (es decir, para "qué hacer" y "Cómo hacer una escala de" por separado ". Se considera desde una perspectiva de diseño.
"La capacidad de usar la misma expresión para denotar diferentes operaciones se conoce como polimorfismo" (Métodos orientados a objetos Principios y práctica 3ª edición, p. 16). En pocas palabras, el polimorfismo es "la misma expresión, diferentes operaciones", o se puede decir que "el mismo comando, diferentes operaciones". Esto es desde la perspectiva de la semántica orientada a objetos.
Las tres declaraciones explican la esencia del polimorfismo desde diferentes perspectivas. La tercera declaración es particularmente precisa.
Permítanme explicar primero el significado de esta oración:
Misma expresión: llamada de función
Diferentes operaciones: hay operaciones diferentes según diferentes objetos.
Por ejemplo, en una empresa, hay varios empleados con diferentes responsabilidades (programadores, vendedores, administradores de documentos, etc.) que hacen cosas diferentes cuando "van a trabajar" (también se puede considerar como una especie de lógica comercial), nosotros Abrazar su trabajo respectivo como "trabajar", y la relación es la siguiente:
personal
/ |
Programador Vender Manager de documentos
Una vez que llegan las horas de trabajo todos los días, es equivalente a emitir un comando como este:
"Empleados. Empiece a trabajar" (misma expresión)
Después de que cada empleado recibe este comando (el mismo comando), "comienza a trabajar", pero lo que hace es su trabajo respectivo, el programador comienza a "codificar", el vendedor comienza "negocios de contacto" y el gerente cultural comencemos " Organización de documentos ”. Es decir, "la misma expresión (llamada de función), diferentes operaciones (ejecute de acuerdo con diferentes objetos durante el período de tiempo de ejecución)".
Desde la perspectiva de la implementación del lenguaje del polimorfismo, el polimorfismo se logra llamando a su método virtual mediante punteros de clase base o referencias a objetos que apuntan a clases derivadas. La siguiente es la implementación del idioma objeto Pascal
Temployee = class // empleados abstractos en una clase abstracta
público
procedimiento comienza a trabajar; virtual; abstracto;
{Funciones abstractas (es decir, funciones virtuales puras en C ++), no hacer nada, el significado real es reservar una interfaz primero. La sobrecarga lo implementa en su clase derivada. }
fin;
TProgramer = Class (Temloyee) // Programador
público
Procedimiento de inicio;
fin;
Tbusinessman = class (Temployee) // vendedor
público
Procedimiento de inicio;
fin;
TDocManager = class (Temlepleee) // Text Manager
público
Procedimiento de inicio;
fin;
Procedimiento TProgramer.StartWorking;
Comenzar
showMessage ('codificación');
fin;
{Tbusinessman}
procedimiento tbusinessman.Startworking;
Comenzar
showMessage ('vinculación de negocios');
fin;
{TDocmanager}
Procedimiento tDocmanager.StartWorking;
Comenzar
showMessage ('Gestión del documento');
fin;
procedimiento tForm1.Button1Click (remitente: tobject);
estúpido
enum = 3;
varilla
Empleado: matriz de Temployee;
I: entero;
Comenzar
setLength (empleado, enum);
Empleado [0]: = TProgramer.Create;
// Consulte al empleado de la clase base [0] para señalar el objeto TProgramer que acaba de crear
Empleado [1]: = tbusinessman.create;
// Consulte al empleado de la clase base [1] para señalar el objeto Tbusinessman que acaba de crear
Empleado [2]: = tDocmanager.create;
// Consulte al empleado de la clase base [2] para señalar el objeto tDocmanager que acaba de crear
para i: = 0 a longitud (empleado) -1 hacer
Empleado [i].
{Desde la perspectiva del polimorfismo de implementación del lenguaje, el polimorfismo se implementa llamando a su método virtual por puntero de clase base o objeto de referencia que apunta a clases derivadas. El empleado [] se refiere a una matriz para el objeto de clase base, y sus miembros apuntan a diferentes objetos de clase derivados respectivamente.
fin;
Probar
Puede escribir parte del código anterior (o programas de demostración), compilar y ejecutar, y hacer clic en el botón para ver el efecto mágico del polimorfismo.
1.2 El significado del polimorfismo
El significado de encapsulación y herencia es que implementan la reutilización de código, mientras que el significado del polimorfismo es que implementa la reutilización de la interfaz (la misma expresión). Capaz.
Por ejemplo, para administrar mejor, los programadores se dividen en programadores de C ++ y programadores de Delphi. …
personal
/ |
Programador Vender Manager de documentos
/ / — - Relación de herencia
Programador de C ++ Delphi Programador
Después de que el programador agregó las dos clases derivadas de tcppprogramer y tdelphiprogramer, el método de llamadas aún no cambió, y todavía era "empleados. Comience a trabajar", que se describió con Object Pascal:
…
setLength (empleado, enum+2);
Empleado [enum]: = tcppprogramer.create;
// Cree un objeto TCPPProgramer y apunte al empleado de referencia de clase base [enum]
Empleado [enum+1]: = tdelphiprogramer.create;
…
{Sectores.
para i: = 0 a longitud (empleado) -1 hacer
Empleado [i] .Working;
…
1.3 ¿Cómo implementar el polimorfismo en Delphi?
Las condiciones necesarias para lograr el polimorfismo son la herencia, los métodos virtuales, la unión dinámica (o la unión de retraso en el retraso).
1.3.1 Herencia
La herencia se refiere a la relación "AKO (una especie de, es a)" entre una clase, como un programador "es un" empleado, que indica una relación de herencia. En Delphi, solo se admite la herencia única (sin consideración de la herencia múltiple implementada por las interfaces). Por objetos de clase derivados (o viceversa).
pista
En uml:
AKO: una especie de relación de herencia de medios
Apo: una parte de representa una relación combinada
ISA: ISA representa la relación entre un objeto y una clase a la que pertenece.
1.3.2 Métodos virtuales, métodos dinámicos y métodos abstractos, VMT/DMT, enlace estático y enlace dinámico
Para todos los métodos, no hay rastro en el objeto. Su puntero de método (dirección de entrada) se almacena en la clase, mientras que el código real se almacena en el segmento de código. Para los métodos estáticos (métodos no virtuales), el compilador determina directamente la dirección de entrada del método del objeto basado en el tipo de referencia del objeto en el tiempo de compilación, que se llama enlace estático; El compilador no puede determinar la clase real a la que pertenece, por lo que la dirección de entrada del método solo se puede determinar a través de la dirección de entrada de la tabla VMT (es decir, los primeros cuatro bytes del objeto) durante el tiempo de ejecución. Bunding de retraso).
Método virtual
Un método virtual representa un método que se puede anular. Además de almacenar su propio puntero de método virtual, la clase también almacena un puntero de método virtual de todas las clases base.
Método de declaración:
Nombre del método de procedimiento;
Esto es equivalente a decirle al compilador de Delphi:
Este método se puede sobrecargar en la clase derivada, y el método virtual seguirá siendo después de sobrecargar.
No determine la dirección de entrada del método durante el período de compilación. Durante el tiempo de ejecución, la dirección de entrada del método se determina a través de la unión dinámica.
Proporcione una implementación predeterminada en la clase base.
Método dinámico
Los métodos dinámicos y los métodos virtuales son esencialmente los mismos. Pero esto es completamente transparente para los usuarios.
Método de declaración:
nombre del procedimiento; dinámico;
Métodos abstractos
Un método virtual especial, que no necesita proporcionar una implementación predeterminada en la clase base, solo se usa para una interfaz de llamada, que es equivalente a una función virtual pura en C ++. Las clases que contienen métodos abstractos se denominan clases abstractas.
Método de declaración:
nombre del procedimiento; virtual; abstracto;
VMT/DMT
En Delphi, la tabla de métodos virtuales (VMT) en realidad no es físicamente. Métodos virtuales de su clase base. La "dirección de entrada VMT" almacenada en los primeros cuatro bytes del objeto es en realidad la dirección de la clase a la que pertenece (ver el programa de demostración). Con la clase real, se puede encontrar la dirección del método virtual.
Obj (nombre del objeto) la clase a la que pertenece el objeto real
Dirección de entrada de VMT
Miembros de datos
Dirección de entrada VMT de la tabla de métodos virtuales de clase
Información de plantilla de miembro de datos
Métodos estáticos, etc.
Método virtual (VMT)
Método dinámico (DMT)
Figura 3 La relación entre nombres de objetos, objetos y clases
DMT es similar a VMT, y también es un concepto lógico. no es tan rápido como el de los métodos virtuales.
Cita el ejemplo anterior para explicar:
Empleado [i] .Working;
El empleado [i] es una referencia de objeto de la clase base TempleEyee. Por lo tanto, el objeto real no se puede conocer durante la compilación, por lo que la dirección del método no se puede determinar. Durante el período de ejecución, por supuesto, conozco la "cara verdadera de Lushan" del objeto. La función a llamar, es decir, el polimorfismo se realiza.
1.3.3 Sobrecarga y polimorfismo
Muchos internautas creen que la sobrecarga de funciones también es un tipo de polimorfismo, pero no lo es. Para "operaciones diferentes", la sobrecarga no puede proporcionar el mismo método de llamada. ¡La premisa de implementar el polimorfismo es la misma expresión! Por ejemplo, el empleado [i]. La sobrecarga es solo un mecanismo de lenguaje, y también hay sobrecarga en C, pero C no tiene polimorfismo y C no es un lenguaje de programación orientado a objetos. A menos que la función de sobrecarga también sea un método virtual, ¡el compilador puede determinar la dirección de entrada de la función basada en el tipo de parámetro o la unión estática! Cita al padre de C ++, "No seas estúpido. Si no es un enlace dinámico, no es polimorfismo".
1.4 Discusión sobre especies polimórficas
1.4.1 polimorfismo de dos niveles
Nivel de objeto: use el puntero/referencia de clase base para apuntar a su objeto de clase derivada y los métodos virtuales de llamadas (o métodos dinámicos, métodos abstractos), que es el más utilizado.
Nivel de clase: Use referencias de clase (referencias a clases en lugar de objetos) para señalar clases derivadas, llamar a métodos de clase virtual (o métodos de clase dinámica, métodos de clase abstracta), y se usan comúnmente en los polimorfismos creados por objetos (porque el constructor es es Un tipo de métodos de tipo "para especiales", consulte mi otro trabajo "Análisis de la estructura y destrucción en Delphi", Sección 2.1).
pista
La referencia de clase es una variable de referencia de la clase misma, no una clase, ni una referencia de objeto. Al igual que el nombre del objeto representa una referencia de objeto, el nombre de clase representa una referencia de clase, porque en Delphi, la clase también se procesa como un objeto. Un tipo de referencia de clase es el tipo de referencia de clase y el método de declaración de un tipo de referencia de clase:
Tipo de referencia de clase Nombre = Clase de nombre de clase
Podemos ver muchas declaraciones de referencia de clase en el código fuente de VCL, como:
TcLass = clase de tobject;
TComponentClass = clase de tComponent;
TControlClass = class of tControl;
Aviso
En un método de clase, el yo implícito en el método es una referencia de clase, no una referencia de objeto.
1.4.2 Polimorfismo inseguro
¡El polimorfismo también se puede implementar utilizando punteros de clase derivados/referencias a objetos de clase base!
procedimiento tform1.btnbadpolyClick (remitente: tobject);
varilla
CPPProgramer: TCPPProgramer; // Defina una referencia del programador CPP, ¡una referencia a una clase derivada!
Comenzar
{*****************************declaración******************* ********************
El polimorfismo implementado con punteros de clase derivados/referencias a objetos de clase base. ¡Es un polimorfismo patológico!
Este método polimórfico es como algo muy pequeño (objeto de clase base) que está cubierto con uno poderoso
La apariencia del producto (referencias de clase derivadas) trae muchos factores de inseguridad potenciales (como las excepciones de acceso), por lo tanto
Casi sin valor. "Archivo" Un ejemplo tiene como objetivo ilustrar la naturaleza del polimorfismo en Delphi, la naturaleza del polimorfismo: use un puntero/referencia legal (generalmente la clase base) para operar el objeto, de acuerdo con el objeto real durante el tiempo de ejecución, para ejecutar una operación diferente Métodos, o a una declaración más vívida: el objeto en sí decide su propio método de operación. el objeto. Esto permite la separación de interfaces e implementaciones, haciendo posible la reutilización de la interfaz.
************************************************************ ******* ********************************}
cppprogramer: = tcppprogramer (tprogramer.create);
{Para lograr este polimorfismo patológico, la referencia del objeto se emite al tipo tcppprogramer,
así escapa de la verificación del compilador}
cppProgramer.StartWorking;
{Tprogramer.startworking llamado en lugar de tcppprogramer.Startworking
Este es el polimorfismo implementado con punteros de clase derivados/referencias al objeto de clase base. }
cppprogramer.free;
cppprogramer: = tcppprogramer (tdocmanager.create);
cppProgramer.StartWorking;
{La llamada es tdocmanager.startworking,
Este es el polimorfismo implementado con punteros de clase derivados/referencias al objeto de clase base. Este método es extremadamente inseguro.
Y no hay necesidad}
cppprogramer.free;
fin;
Probar
Para obtener este tipo de comprensión perceptiva del polimorfismo, se recomienda probarlo anteriormente. ? ¿En qué circunstancias ocurrirá una excepción de acceso? (Consulte el programa de demostración)
2 Aplicaciones de polimorfismo en VCL
2.1 Métodos de construcción y destrucción
Polimorfismo del método de construcción
Dado que el constructor puede considerarse como un método de clase "especial", todas las clases derivadas después de que el TComponente se redefinea como métodos de clase virtual, por lo que para realizar el polimorfismo del constructor, debe usar referencias de clase. En cada archivo de proyecto hay un código similar al siguiente:
Application.CreateForm (TForm1, Form1);
Definición de su método:
procedimiento TAPPLICATION.CreateForm (InstancECLass: TComponentClass; VAR Reference);
Var // Instanceclass es una referencia de clase.
Instancia: tcomponent;
Comenzar
Instancia: = tComponent (instanceclass.newinstance);
{Declaración del método de NewInstance: Función de clase NewInstance: Tobject; Instanceclass es una referencia de clase que implementa el polimorfismo a nivel de clase, realizando así la reutilización de la interfaz para crear componentes}
TComponent (referencia): = instancia;
intentar
Instance.create (self); // llame al constructor para inicializarlo
excepto
Tcomponent (referencia): = nil; // ¡elimine el puntero "salvaje"! bien
aumentar;
fin;
{Si la ventana creada y aún no hay forma principal, establezca el formulario recién creado como el formulario principal}
if (fmainform = nil) y (la instancia es tform) entonces
Comenzar
TForm (instancia) .HandLeneeded;
Fmainform: = tform (instancia); // Establecer el formulario principal
{De hecho, establecer el formulario principal en las opciones del proyecto (Opciones de proyecto->) es en realidad la declaración de formulario correspondiente en el archivo del proyecto antes de todas las declaraciones de creación de formulario. }
fin;
fin;
2) Para el polimorfismo del método de destrucción, consulte "Análisis de la estructura y destrucción en Delphi", Sección 3.3
2.2 tstrings
El procesamiento de la matriz de cadenas es muy común en los controles de Delphi, generalmente con algunos atributos de elementos, que son particularmente convenientes para nosotros (porque todos usan la misma interfaz), gracias al diseño de la arquitectura de matriz de cuerdas en Delphi. Este es un diseño exitoso.
Dado que muchos controles usan matrices de cadenas, como ComboBox, TStringGrid, etc., pero las matrices de cadenas en cada control son diferentes, Delphi abstra las matrices de cadenas, por lo tanto, aparecen muchas clases relacionadas. La clase base TStrings solo proporciona una interfaz para varias llamadas, y la implementación específica se puede implementar por completo de sus clases derivadas.
Echemos un vistazo a la definición de métodos comunes de la clase de clase base TStrings (ver Classes Unit Line 442):
Tstrings = class (tpersistent)
protegido
...
función get (índice: entero): string;
procedimiento PUT (índice: entero; const s: string);
función getCount: entero;
…
público
function add (const s: string): entero;
{Agregue una cadena S al final de la lista de cadenas}
procedimiento addstrings (cadenas: tstrings);
{Agregar cadenas al final de la lista de cadenas}
Procedimiento Insertar (índice: Integer; const S: String);
{Método abstracto, inserte una nueva cadena S en la posición de índice}
procedimiento claro;
{Borrar todas las cuerdas}
procedimiento eliminar (índice: entero);
{Eliminar una cadena en una ubicación determinada}
función indexOf (const s: string): entero;
{Obtenga la posición de S en la lista de cadenas}
función indexOfName (const name: string): entero;
{Devuelve la posición de la primera cadena con el nombre de formulario = valor con la parte del nombre especificado}
función indexOfObject (aObject: tobject): entero;
{Obtenga la posición del objeto con el objeto llamado Aobject: en la lista de cadenas}
procedimiento loadFromFile (const fileName: string);
{Llena la lista con las líneas de texto en un archivo especificado}
procedimiento loadFromStream (Stream: tstream);
{Llena la lista con líneas de texto leídas de una transmisión}
procedimiento SaveToStream (Stream: tstream);
{Escribe el valor de la propiedad de texto a un objeto de transmisión}
Cadenas de propiedades [índice: entero]: léase de cadena obtenga escritura PUNTO;
{Referencias las cadenas en la lista por sus posiciones}
Valores de propiedad [Nombre const: String]: String Read getValue Write setValue;
{Representa la parte del valor de una cadena asociada con un nombre de determinación, en cadenas con el nombre de formulario = valor.}
…
fin;
A partir de la definición de TStrings, se puede ver que la mayoría de sus métodos protegidos y públicos son métodos virtuales o métodos abstractos. (Agregue algunos, tStringList-> tStringGridString)
2.3 otros (por favor agregue el alma)
Si aún no comprende el polimorfismo, recuerde la esencia del polimorfismo:
"Misma expresión, diferentes operaciones" (es así de simple)
Desde la perspectiva de la implementación del lenguaje OOP, el polimorfismo es utilizar el puntero/referencia de la clase base para operar objetos (clase derivada) y realizar diferentes métodos de operación de acuerdo con el objeto real durante el período de ejecución; Una declaración más vívida: el objeto decide su propio método de operación. sí mismo. Esto permite la separación de interfaces e implementaciones, haciendo posible la reutilización de la interfaz.
De hecho, ¡el polimorfismo es simple! Entonces, ¿a qué debe prestar atención al usar el polimorfismo? Aquí están mis dos sugerencias:
Analice la lógica de negocios, luego abstraga las cosas relacionadas en "objetos" y luego encapsulen la lógica comercial utilizando métodos de objetos. Declare algunas operaciones con el polimorfismo como métodos virtuales en la clase base, y declararlas como métodos abstractos virtuales para aquellos que no son necesarios para implementar en la clase base, y luego sobrecargarlos en sus clases derivadas. /Punteros de la clase base cuando se usa, lo que naturalmente realiza el polimorfismo en el mundo real. Recuerde no lograr el polimorfismo en aras del polimorfismo.
Dado que existe una relación natural de "acoplamiento" entre las clases base y las clases derivadas, modificar las clases base conducirá a "llegar a todo el cuerpo", ¡lo cual será muy problemático! Por lo tanto, debemos tratar de debilitar la implementación funcional de la clase base, diseñarla como una "clase abstracta" si es necesario y garantizar una interfaz estable que esto se pueda lograr reservando algunas funciones virtuales redundantes (o funciones abstractas).
Preguntas relacionadas
Discuta el polimorfismo de Delphi: http://www.delphhibbs.com/delphhibbs/dispq.asp?lid=1753965
Acerca del polimorfismo: http://www.delphhibbs.com/delphhibbs/dispq.asp?lid=1854895
¿Qué es el polimorfismo? ¿Cuáles son los usos en la programación diaria? http://www.delphibbs.com/delphibbs/dispq.asp?lid=960465
¿Cuál es la diferencia entre sobrecarga y anulación? http://www.delphibs.com/delphibs/dispq.asp?lid=296739
Problema que el puntero de la clase derivada apunta al objeto de clase base http://www.delphibs.com/delphibs/dispq.asp?lid=2104106
(La última pregunta se mencionó sobre Delphibbs cuando estaba estudiando profundamente el polimorfismo, lo que causó una discusión acalorada. Le sugiero que eche un vistazo)