Análise de polimorfismos em Delphi
1 O que é o polimorfismo? 2
1.1 Conceito 2
1.2 O significado do polimorfismo 2
1.3 Como implementar o polimorfismo em Delphi? 2
1.3.1 Herança 2
1.3.2 Métodos virtuais, métodos dinâmicos e métodos abstratos, VMT/DMT, ligação estática e ligação dinâmica 2
1.3.3 Sobrecarga e polimorfismo 2
1.4 Discussão sobre espécies polimórficas 2
1.4.1 Polimorfismo de dois níveis 2
1.4.2 Polimorfismo inseguro 2
2 aplicações de polimorfismo em VCL2
2.1 Método de construção e destruição 2
2.2 TStrings2
2.3 Outros (por favor adicione Soul) 2
O polimorfismo abstrato é a alma da orientação de objetos.
Polimorfismo de palavra-chave , herança, orientação a objetos, VCL, método virtual, substituição
pergunta
O polimorfismo é a alma da orientação de objetos, e a compreensão do polimorfismo é uma das chaves para dominar a tecnologia orientada a objetos. Mas o que exatamente é o polimorfismo? Qual é o significado do polimorfismo? Como alcançar o polimorfismo? Eu posso entender o conceito de polimorfismo, mas não sei como usá -lo e quando usá -lo? Por favor, leia este artigo em detalhes.
Análise de especialistas
O mundo (objeto, isto é, objeto) está sempre em mudança; Bem, na linguagem do computador. Indústria de software inteira foi a troca de terra. As ferramentas de desenvolvimento orientadas também apareceram, como VC, Delphi e BCB. Design orientado (OOD), análise orientada a objetos (OOA) e banco de dados orientado a objetos (OODB)), a tecnologia orientada a objetos quase penetrou em todo o campo de software, e o modo de pensar dos programadores também sofreu mudanças fundamentais! Aos olhos de alguns teóricos da purificação de OO, tudo é um objeto! Embora eu não concorde com essa visão. Mas acho que esse método está de acordo com os hábitos de pensamento das pessoas Cérebro a partir de então, fui libertado! Esta é uma revolução!
O conteúdo central do objeto é o objeto, o encapsulamento, a herança, o polimorfismo e os mecanismos de mensagem. Polimorfismo mestre, você não dominará realmente a tecnologia orientada a objetos.
1 O que é o polimorfismo?
1.1 Conceito
Existem muitas opiniões diferentes sobre o conceito de polimorfismo, e a seguir são várias declarações representativas:
“Essa capacidade de manipular mais de um tipo com um ponteiro ou uma referência a uma classe base fala como polimorfismo” (C ++ Primer, página 838). Ou seja, a capacidade de operar objetos de várias classes (classe base e suas classes derivadas) usando ponteiros/referências às classes base é chamada polimorfismo. É considerado da perspectiva da implementação do idioma.
"O polimorfismo fornece outra dimensão da separação da interface da implementação, para dissociar o que de como" ("Pense em Java" 3ª edição), ou seja, o polimorfismo fornece outro tipo de interface e implementação de separação (isto é, "o que fazer" e "Como fazer uma escala de" separadamente ". É considerado do ponto de vista do design.
“A capacidade de usar a mesma expressão para denotar operações diferentes é referida como polimorfismo” (Métodos Orientados a Objetos Principles & Practice 3rd Edition, p. 16). Simplificando, o polimorfismo é "a mesma expressão, operações diferentes", ou pode -se dizer que "o mesmo comando, operações diferentes". Isso é da perspectiva da semântica orientada a objetos.
As três declarações explicam a essência do polimorfismo de diferentes perspectivas. A terceira declaração é particularmente precisa.
Deixe -me explicar o significado desta frase primeiro:
Mesma expressão - chamada de função
Operações diferentes - existem operações diferentes de acordo com diferentes objetos.
Por exemplo, em uma empresa, existem vários funcionários com diferentes responsabilidades (programadores, vendedores, gerentes de documentos etc.) que fazem coisas diferentes quando "vão trabalhar" (também podem ser consideradas um tipo de lógica de negócios), nós Resumo O seu respectivo trabalho como "trabalhar", e o relacionamento é o seguinte:
funcionários
/ |
Programador de vendedor de programador gerente de documentos
Quando as horas de trabalho chegam todos os dias, é equivalente a emitir um comando como este:
"Funcionários. Comece a trabalhar" (mesma expressão)
Depois que cada funcionário recebe esse comando (o mesmo comando), ele "começa a trabalhar", mas o que eles fazem é o seu respectivo trabalho, o programador começa a "codificação", o vendedor inicia o "negócio de contato" e o gerente cultural vamos começar " organização de documentos ”. Ou seja, "a mesma expressão (chamada de função), diferentes operações (executadas de acordo com diferentes objetos durante o período de tempo de execução)".
Do ponto de vista da implementação da linguagem do polimorfismo, o polimorfismo é alcançado chamando seu método virtual por ponteiros de classe base ou referências a objetos que apontam para classes derivadas. A seguir, a implementação da linguagem Pascal do objeto
Temploee = classe // Abstract Funcionários em uma classe abstrata
público
Procedimento StartWorking; virtual; abstrato;
{Funções abstratas (isto é, funções virtuais puras em C ++), não faça nada, o significado real é reservar uma interface primeiro. A sobrecarga o implementa em sua classe derivada. }
fim;
TProgramer = classe (temploeee) // programador
público
Procedimento Startworking;
fim;
Tbusinessman = classe (temploeu) // vendedor
público
Procedimento Startworking;
fim;
TdocManager = classe (temploeee) // gerenciador de texto
público
Procedimento Startworking;
fim;
procedimento tProgramer.startworking;
Começar
showMessage ('codificação');
fim;
{Tbusinessman}
procedimento tbusinessman.startworking;
Começar
showMessage ('Linking Business');
fim;
{Tdocmanager}
procedimento tdocmanager.startworking;
Começar
showMessage ('Managing Document');
fim;
procedimento TForm1.Button1Click (remetente: TOBJECT);
const
enum = 3;
var
Empregado: Matriz de Temployee;
I: Inteiro;
Começar
setLength (funcionário, enum);
Funcionário [0]: = tProgramer.create;
// Consulte o funcionário da classe base [0] para apontar para o objeto TPROGRAMER que acabou de ser criado
Funcionário [1]: = tbusinessman.create;
// Consulte o funcionário da classe base [1] para apontar para o objeto Tbusinessman que você acabou de criar
Funcionário [2]: = tdocmanager.create;
// Consulte o funcionário da classe base [2] para apontar para o objeto TDocManager acabou de ser criado
para i: = 0 para comprimento (funcionário) -1 do
Funcionário [i] .startworking;
{Da perspectiva do polimorfismo de implementação de idiomas, o polimorfismo é implementado chamando seu método virtual por ponteiro da classe base ou objeto de referência apontando para classes derivadas. O funcionário [] refere -se a uma matriz para o objeto de classe base, e seus membros apontam para diferentes objetos de classe derivada, respectivamente.
fim;
Experimente
Você pode digitar alguns dos código acima (ou programas de demonstração), compilar e executar e clicar no botão para ver o efeito mágico do polimorfismo.
1.2 O significado do polimorfismo
O significado de encapsulamento e herança é que eles implementam a reutilização do código, enquanto o significado do polimorfismo é que ele implementa a reutilização da interface (a mesma expressão). capaz de flexibilidade pode realmente refletir o mundo real.
Por exemplo, para gerenciar melhor, os programadores são divididos em programadores C ++ e programadores Delphi. ...
funcionários
/ |
Programador de vendedor de programador gerente de documentos
/ / —— Relacionamento de herdância
Programador C ++ Programador Delphi
Depois que o programador adicionou as duas classes derivadas do TCPPPROGRAMER e TDELPHIPROGRAMER, o método de chamada ainda não mudou e ainda era "funcionários. Comece a trabalhar", que foi descrito com o objeto Pascal:
...
setLength (funcionário, enum+2);
Funcionário [enum]: = tcppProgramer.create;
// Crie um objeto TCPPPROGRAMER e aponte o funcionário de referência da classe base [enum]
Funcionário [enum+1]: = tdelphiprogramer.create;
...
{Setores.
para i: = 0 para comprimento (funcionário) -1 do
Funcionário [i] .startworking;
...
1.3 Como implementar o polimorfismo em Delphi?
As condições necessárias para alcançar o polimorfismo são herança, métodos virtuais, ligação dinâmica (ou ligação de atraso).
1.3.1 Herança
A herança refere -se ao relacionamento "Ako (uma espécie de, é a)" "entre uma classe, como um programador" é um funcionário ", indicando um relacionamento de herança. Em Delphi, apenas a herança única é suportada (nenhuma consideração de herança múltipla implementada por interfaces). por objetos de classe derivados (ou vice-versa).
dica
Em uml:
Ako: um tipo de relacionamento de herança de meios
Apo: uma parte de representa um relacionamento de combinação
Isa: Isa representa a relação entre um objeto e uma classe a que pertence.
1.3.2 Métodos virtuais, métodos dinâmicos e métodos abstratos, VMT/DMT, ligação estática e ligação dinâmica
Para todos os métodos, não há vestígios no objeto. Seu ponteiro de método (endereço de entrada) é armazenado na classe, enquanto o código real é armazenado no segmento de código. Para métodos estáticos (métodos não virtuais), o compilador determina diretamente o endereço de entrada do método do objeto com base no tipo de referência do objeto no tempo de compilação, que é chamado de ligação estática; O compilador não pode determinar a classe real à qual pertence, portanto, o endereço de entrada do método só pode ser determinado através do endereço de entrada da tabela VMT (ou seja, os quatro primeiros bytes do objeto) durante o tempo de execução. lag-bundling).
Método virtual
Um método virtual representa um método que pode ser substituído. Além de armazenar seu próprio ponteiro de método virtual, a classe também armazena ponteiro de método virtual de todas as classes base.
Método da declaração:
Nome do método do procedimento;
Isso é equivalente a dizer ao compilador Delphi:
Este método pode ser sobrecarregado na classe derivada, e o método virtual ainda será após a sobrecarga.
Não determine o endereço de entrada do método durante o período de compilação. Durante o tempo de execução, o endereço de entrada do método é determinado através da ligação dinâmica.
Forneça uma implementação padrão na classe base.
Método dinâmico
Os métodos dinâmicos e os métodos virtuais são essencialmente os mesmos. Mas isso é completamente transparente para os usuários.
Método da declaração:
Nome do procedimento; dinâmico;
Métodos abstratos
Um método virtual especial, que não precisa fornecer uma implementação padrão na classe base, é usada apenas para uma interface de chamada, que é equivalente a uma função virtual pura no C ++. As classes contendo métodos abstratos são chamados de classes abstratas.
Método da declaração:
Nome do procedimento; virtual; Resumo;
VMT/DMT
Em Delphi, a tabela de método virtual (VMT) não é fisicamente. Métodos virtuais de sua classe base. O "endereço de entrada do VMT" armazenado nos quatro primeiros bytes do objeto é na verdade o endereço da classe a que pertence (consulte o programa Demo). Com a classe real, o endereço do método virtual pode ser encontrado.
Obj (nome do objeto) a classe à qual o objeto real pertence
Endereço de entrada VMT
Membros de dados
Endereço de entrada VMT da tabela de método virtual de classe
Informações do modelo de membro de dados
Métodos estáticos, etc.
Método Virtual (VMT)
Método dinâmico (DMT)
Figura 3 A relação entre nomes de objetos, objetos e classes
O DMT é semelhante ao VMT e também é um conceito lógico. não é tão rápido quanto o dos métodos virtuais.
Cite o exemplo acima para explicar:
Funcionário [i] .startworking;
O funcionário [i] é uma referência de objeto da classe base TempleEyee. Portanto, o objeto real não pode ser conhecido durante a compilação; portanto, o endereço do método não pode ser determinado. Durante o período de execução, é claro, conheço a "face verdadeira de Lushan" do objeto. A função a ser chamada, isto é, o polimorfismo é realizado.
1.3.3 Sobrecarga e polimorfismo
Muitos internautas acreditam que a sobrecarga da função também é um tipo de polimorfismo, mas não é. Para "operações diferentes", a sobrecarga não pode fornecer o mesmo método de chamada. A premissa de implementar o polimorfismo é a mesma expressão! Por exemplo, o funcionário [i] .startworing, chamadas sobrecarregadas têm parâmetros diferentes ou tipos de parâmetros. A sobrecarga é apenas um mecanismo de idioma e também há sobrecarga em C, mas C não possui polimorfismo e C não é uma linguagem de programação orientada a objetos. A menos que a função de sobrecarga também seja um método virtual, o compilador pode determinar o endereço de entrada da função com base no tipo de parâmetro ou na ligação estática! Cite o pai de C ++: "Não seja estúpido. Se não é uma ligação dinâmica, não é polimorfismo".
1.4 Discussão sobre espécies polimórficas
1.4.1 Polimorfismo de dois níveis
Nível do objeto: use o ponteiro/referência da classe base para apontar para o objeto de classe derivado e chamar métodos virtuais (ou métodos dinâmicos, métodos abstratos), que é o mais comumente usado um.
Nível de classe: use referências de classe (referências a classes em vez de objetos) para apontar para classes derivadas, chamar métodos de classe virtual (ou métodos de classe dinâmica, métodos de classe abstrata) e são comumente usados nos polimorfismos criados por objetos (porque o construtor é Uma espécie de métodos do tipo "para especial", consulte a análise do meu outro trabalho "da estrutura e destruição em Delphi", Seção 2.1).
dica
A referência de classe é uma variável de referência da própria classe, não uma classe, nem uma referência de objeto. Assim como o nome do objeto representa uma referência de objeto, o nome da classe representa uma referência de classe, porque em Delphi, a classe também é processada como um objeto. Um tipo de referência de classe é o tipo de referência de classe e o método de declaração de um tipo de referência de classe:
Nome do tipo de referência da classe = classe de nome de classe
Podemos ver muitas declarações de referência de classe no código -fonte VCL, como:
Tclass = classe de tabjas;
TComponentClass = classe de TComponent;
TControlClass = classe de tControl;
Perceber
Em um método de classe, o auto -implícito no método é uma referência de classe, não uma referência de objeto.
1.4.2 Polimorfismo inseguro
O polimorfismo também pode ser implementado usando ponteiros de classe derivados/referências a objetos de classe base!
procedimento tform1.btnbadpolyclick (remetente: tabjas);
var
cppProgramer: tcppProgramer; // Defina uma referência de programador de CPP, uma referência a uma classe derivada!
Começar
{*****************************declaração******************* ********************
O polimorfismo implementado com ponteiros de classe derivados/referências a objetos de classe base. É um polimorfismo patológico!
Esse método polimórfico é como uma coisa muito pequena (objeto de classe base) que é coberto com um poderoso
A aparência do produto (referências de classe derivada) traz muitos fatores de insegurança em potencial (como exceções de acesso), então
Quase sem valor. "Arquivo" Um exemplo tem como objetivo ilustrar a natureza do polimorfismo em Delphi, a natureza do polimorfismo: use um ponteiro/geralmente a classe base) referência/referência para operar o objeto, de acordo com o objeto real durante o tempo de execução, para executar a operação diferente Métodos, ou para uma declaração mais vívida: o próprio objeto decide seu próprio método de operação. o objeto. Isso permite a separação de interfaces e implementações, possibilitando a reutilização da interface.
**************************************************** ******* *********************************}
cppProgramer: = tcppProgramer (tProgramer.create);
{Para alcançar esse polimorfismo patológico, a referência de objeto é fundida ao tipo TCPPPROGRAMER,
assim escapa da verificação do compilador}
cppProgramer.startworking;
{TProgramer.startworking chamado em vez de tcppProgramer.startworking
Este é o polimorfismo implementado com ponteiros de classe derivados/referências ao objeto de classe base. }
cppProgramer.Free;
cppProgramer: = tcppProgramer (tdocmanager.create);
cppProgramer.startworking;
{A chamada é tdocmanager.startworking,
Este é o polimorfismo implementado com ponteiros de classe derivados/referências ao objeto de classe base. Este método é extremamente inseguro.
E não há necessidade}
cppProgramer.Free;
fim;
Experimente
Para obter esse tipo de compreensão perceptiva do polimorfismo, é recomendável experimentar ? Em que circunstâncias ocorrerão uma exceção de acesso? (Consulte o programa de demonstração)
2 aplicações de polimorfismo em VCL
2.1 Métodos de construção e destruição
Polimorfismo do método de construção
Como o construtor pode ser considerado um método de classe "especial", todas as classes derivadas após o Tomponente são redefinidas como métodos de classe virtual, para realizar o polimorfismo do construtor, você deve usar referências de classe. Em cada arquivo de projeto, há um código semelhante ao seguinte:
Application.CreatEform (TForm1, Form1);
Definição de seu método:
Procedimento TapPplication.CreateForm (Instanceclass: TComponentClass; VAR Reference);
var // Instanceclass é uma referência de classe.
Instância: TComponent;
Começar
Instância: = tComponent (Instanceclass.NewInstance);
{Declaração do Método de NewInstance: função de classe NewInstance: Tobject; Instanceclass é uma referência de classe que implementa o polimorfismo no nível da classe, realizando a reutilização da interface para criar componentes}
TComponent (referência): = instância;
tentar
Instance.create (self); // chama o construtor para inicializá -lo
exceto
TComponent (Referência): = nil; // elimina o ponteiro "selvagem"! bom
elevação;
fim;
{Se a janela criada e ainda não há formulário principal, defina o formulário recém -criado como o formulário principal}
if (fmainform = nil) e (a instância é tform) então
Começar
Tform (instância) .HandleNeeded;
Fmainform: = tform (instância); // Defina o formulário principal
{De fato, definir o formulário principal nas opções do projeto (Projeto-> Opções) é na verdade a instrução de formulário correspondente no arquivo do projeto antes de todas as instruções de criação de formulários. }
fim;
fim;
2) Para o polimorfismo do método de destruição, consulte "Análise da estrutura e destruição em Delphi", Seção 3.3
2.2 TSTRINGS
O processamento da matriz de string é muito comum nos controles da Delphi, geralmente com alguns atributos de itens, que são particularmente convenientes para nós usarmos (porque todos usam a mesma interface), graças ao design da arquitetura da matriz de strings em Delphi. Este é um design de sucesso.
Como muitos controles usam matrizes de strings, como ComboBox, TStringGrid, etc., mas as matrizes de string em cada controle são diferentes, o Delphi abstrai as matrizes de string, assim muitas classes relacionadas aparecem. A classe base TSTRINGS fornece apenas uma interface para várias chamadas, e a implementação específica pode ser implementada inteiramente de suas classes derivadas.
Vamos dar uma olhada na definição de métodos comuns da classe TStrings da classe base (consulte Classes Unit Line 442):
TStrings = classe (tpersistente)
protegido
...
função get (Índice: Inteiro): String;
Procedimento Put (índice: Inteiro; const S: String);
função getCount: Inteiro;
...
público
função add (const s: string): Inteiro;
{Adicione uma string s ao final da lista de string}
Addstrings de procedimento (Strings: TSTRINGS);
{Adicione strings ao final da lista de string}
Inserção do procedimento (índice: Inteiro; const S: String);
{Método abstrato, insira uma nova string s na posição de índice}
procedimento claro;
{Limpe todas as cordas}
Procedimento delete (índice: Inteiro);
{Exclua uma string em um determinado local}
função indexOf (const S: string): Inteiro;
{Obtenha a posição de s na lista de string}
função indexOfName (const Nome: String): Inteiro;
{Retorna a posição da primeira string com o nome do formulário = valor com a parte do nome especificado}
Função IndexOfobject (AObject: Tobject): Inteiro;
{Obtenha a posição do objeto com o objeto chamado AOBject: na lista de string}
Procedimento LoadFromFile (nome do arquivo const: string);
{Preenche a lista com as linhas de texto em um arquivo especificado}
Procedimento Carregarfromstream (Stream: TStream);
{Preenche a lista com linhas de texto lido de um fluxo}
Procedimento SaveToStream (Stream: TStream);
{Escreve o valor da propriedade de texto em um objeto de fluxo}
Strings de propriedade [Índice: Inteiro]: String Read Get Write Put;
{Referencia as cordas da lista por suas posições}
Valores da propriedade [Nome const: string]: string leia getValue write setValue;
{Representa a parte do valor de uma string associada a um determinado nome, em strings com o nome do formulário = valor.}
...
fim;
A partir da definição de TSTRINGS, pode -se observar que a maioria de seus métodos públicos e protegidos são métodos virtuais ou métodos abstratos. (Adicione alguns, tStringList-> tStringGridString)
2.3 Outros (por favor, adicione Soul)
Se você ainda não entende o polimorfismo, lembre -se da essência do polimorfismo:
"Mesma expressão, operações diferentes" (é tão simples)
Da perspectiva da implementação da linguagem OOP, o polimorfismo é usar o ponteiro/referência da classe base para operar objetos (classe derivada) e executar diferentes métodos de operação de acordo com o objeto real durante o período de execução; Uma declaração mais vívida: o objeto decide seu próprio método de operação. em si. Isso permite a separação de interfaces e implementações, possibilitando a reutilização da interface.
De fato, o polimorfismo é simples! Então, o que você deve prestar atenção ao usar o polimorfismo? Aqui estão minhas duas sugestões:
Analise a lógica de negócios e abstrante as coisas relacionadas em "objetos" e depois encapsule a lógica de negócios usando métodos de objetos. Declare algumas operações com o polimorfismo como métodos virtuais na classe base e os declare como métodos abstratos virtuais para aqueles que não são necessários para implementar na classe base e, em seguida, sobrecarregar -os em suas classes derivadas. /Ponteiros da classe base quando usados, o que naturalmente percebe o polimorfismo no mundo real. Lembre -se de não alcançar o polimorfismo em prol do polimorfismo.
Como existe uma relação natural de "acoplamento" entre classes base e classes derivadas, a modificação das classes base levará a "alcançar todo o corpo", o que será muito problemático! Portanto, devemos tentar enfraquecer a implementação funcional da classe base, projetá -la como uma "classe abstrata", se necessário, e garantir uma interface estável.
Perguntas relacionadas
Discuta o polimorfismo de Delphi: http://www.delphhibbs.com/delphhibbs/dispq.asp?lid=1753965
Sobre o polimorfismo: http://www.delphhibbs.com/delphhibbs/dispq.asp?lid=1854895
O que é polimorfismo? Quais são os usos na programação diária? http://www.delphibbs.com/delphibbs/dispq.asp?lid=960465
Qual é a diferença entre sobrecarga e substituição? http://www.delphibs.com/delphibs/dispq.asp?lid=296739
Problema que o ponteiro da classe derivada aponta para o objeto de classe base http://www.delphibs.com/delphibs/dispq.asp?lid=2104106
(A última pergunta foi mencionada em Delphibbs quando eu estava estudando profundamente o polimorfismo, o que causou discussão acalorada. Sugiro que você dê uma olhada)