POO em Delphi: Herança, Polimorfismo e Exemplos Práticos
Introdução
Programação Orientada a Objetos (POO) é fundamental para desenvolver software organizado, testável e manutenível em Delphi. Este artigo apresenta conceitos centrais de POO aplicados ao Object Pascal, com ênfase em herança, polimorfismo, encapsulamento e boas práticas de design conforme o livro Clean Code. Incluímos exemplos completos e compiláveis, demonstrando como declarar métodos virtuais na classe base e sobrescrevê-los nos descendentes (override). Ao seguir as normas de codificação sugeridas — como prefixo F para campos privados, nomes descritivos e responsabilidade única das classes — você obterá código mais claro e duradouro.
Conceitos fundamentais de POO no Delphi
Nesta seção recapitulamos definições essenciais: classe, objeto, herança, polimorfismo e encapsulamento. No Delphi, classes são tipos de referência derivados de TObject; métodos podem ser declarados como virtual/abstract para permitir sobrescrita (override) em descendentes. Encapsulamento é obtido com seções private/protected/public e propriedades que controlam o acesso a campos internos.
Classes e objetos: convenções e estrutura
Use nomes claros e PascalCase para classes e métodos, prefira campos privados com prefixo F, e exponha apenas o necessário via propriedades e métodos públicos. Declare métodos que poderão ser sobrescritos como virtual na classe base. Concentre responsabilidades para favorecer testes e manutenção.
Exemplo: assinatura de uma classe base
O exemplo abaixo mostra uma estrutura adequada de classe base com métodos virtuais que podem ser sobrescritos por classes filhas.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
unit BankAccounts; interface uses System.SysUtils; type TAccount = class private FOwner: string; protected FBalance: Currency; // protected para permitir uso em descendentes procedure SetBalance(const Value: Currency); virtual; public constructor Create(const AOwner: string; AInitialBalance: Currency = 0); virtual; procedure Deposit(const Amount: Currency); virtual; procedure Withdraw(const Amount: Currency); virtual; function GetBalance: Currency; virtual; property Owner: string read FOwner write FOwner; property Balance: Currency read FBalance write SetBalance; end; implementation { implementação omitida aqui } end. |
Herança e Polimorfismo
Herança permite que classes compartilhem comportamento; polimorfismo permite tratar objetos de tipos diferentes como instâncias da mesma base, chamando o método apropriado em tempo de execução. Em Delphi, isso depende de métodos declarados virtual na base e override nos descendentes.
Exemplo prático: contas bancárias
Segue um exemplo completo e compilável de uma unidade com TAccount (base), TSavingsAccount (poupança) e TCheckingAccount (corrente). Observe que métodos que são sobrescritos são declarados virtual na classe base.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
unit BankAccounts; interface uses System.SysUtils; type EAccountException = class(Exception); TAccount = class private FOwner: string; protected FBalance: Currency; procedure SetBalance(const Value: Currency); virtual; public constructor Create(const AOwner: string; AInitialBalance: Currency = 0); virtual; procedure Deposit(const Amount: Currency); virtual; procedure Withdraw(const Amount: Currency); virtual; function GetBalance: Currency; virtual; property Owner: string read FOwner write FOwner; property Balance: Currency read FBalance write SetBalance; end; TSavingsAccount = class(TAccount) private FInterestRate: Double; public constructor Create(const AOwner: string; AInitialBalance: Currency; AInterestRate: Double); reintroduce; procedure ApplyInterest; virtual; procedure Withdraw(const Amount: Currency); override; end; TCheckingAccount = class(TAccount) private FOverdraftLimit: Currency; public constructor Create(const AOwner: string; AInitialBalance: Currency; AOverdraftLimit: Currency); reintroduce; procedure Withdraw(const Amount: Currency); override; property OverdraftLimit: Currency read FOverdraftLimit write FOverdraftLimit; end; implementation { TAccount } constructor TAccount.Create(const AOwner: string; AInitialBalance: Currency); begin inherited Create; FOwner := AOwner; FBalance := AInitialBalance; end; procedure TAccount.SetBalance(const Value: Currency); begin FBalance := Value; end; procedure TAccount.Deposit(const Amount: Currency); begin if Amount <= 0 then raise EArgumentException.Create('Amount must be positive.'); FBalance := FBalance + Amount; end; procedure TAccount.Withdraw(const Amount: Currency); begin if Amount <= 0 then raise EArgumentException.Create('Amount must be positive.'); if Amount > FBalance then raise EAccountException.Create('Insufficient funds.'); FBalance := FBalance - Amount; end; function TAccount.GetBalance: Currency; begin Result := FBalance; end; { TSavingsAccount } constructor TSavingsAccount.Create(const AOwner: string; AInitialBalance: Currency; AInterestRate: Double); begin inherited Create(AOwner, AInitialBalance); FInterestRate := AInterestRate; end; procedure TSavingsAccount.ApplyInterest; begin FBalance := FBalance + (FBalance * FInterestRate / 100); end; procedure TSavingsAccount.Withdraw(const Amount: Currency); begin // Poupança não permite overdraft; reutiliza validação da base inherited Withdraw(Amount); end; { TCheckingAccount } constructor TCheckingAccount.Create(const AOwner: string; AInitialBalance: Currency; AOverdraftLimit: Currency); begin inherited Create(AOwner, AInitialBalance); FOverdraftLimit := AOverdraftLimit; end; procedure TCheckingAccount.Withdraw(const Amount: Currency); begin if Amount <= 0 then raise EArgumentException.Create('Amount must be positive.'); if Amount > (FBalance + FOverdraftLimit) then raise EAccountException.Create('Exceeds overdraft limit.'); FBalance := FBalance - Amount; end; end. |
Uso polimórfico
Exemplo de uso em um projeto console demonstrando polimorfismo: manipular TAccount referenciando objetos de tipos derivados.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
program DemoAccounts; uses System.SysUtils, BankAccounts; var Acc: TAccount; begin Acc := TSavingsAccount.Create('Alice', 1000.00, 1.5); try Acc.Deposit(500); Acc.Withdraw(200); Writeln('Saldo: ' + CurrToStr(Acc.GetBalance)); // chama o método apropriado finally Acc.Free; end; end. |
Encapsulamento e propriedades
Propriedades controlam acesso a campos internos; use setters/getters para validações. Mantenha campos com prefixo F e evite acesso público direto para preservar invariantes da classe.
Boas práticas e Clean Code aplicadas ao Delphi
Adote nomes claros, métodos curtos, uma responsabilidade por classe, e comentários só quando o código não for autoexplicativo. Separe interfaces e implementações, prefira propriedades em vez de campos públicos e documente pré-condições e exceções. Estas práticas facilitam manutenção e testes.
Dicas concretas
– Prefira nomes como Deposit, Withdraw em vez de Dep, W; – Use exceções específicas para estados de erro; – Declare virtual apenas quando necessário; – Evite duplicação de lógica entre classes.
Depuração, testes e validação
Crie testes unitários cobrindo comportamento polimórfico e casos de exceção. Ao debugar, examine chamadas virtuais e verifique se o método esperado foi executado. Use Test Frameworks como DUnitX para automatizar verificação de contratos (pré e pós-condições).
#Dica do Mestre
Se você mencionar tecnologias adjacentes (UML, REST, JSON) que não são foco central deste artigo, considere que UML é uma linguagem de modelagem para descrever classes e relações. Consulte a documentação e guias externos para aprofundamento: UML (https://en.wikipedia.org/wiki/Unified_Modeling_Language). Para documentação oficial do Delphi e unidades de runtime, consulte a DocWiki da Embarcadero: https://docwiki.embarcadero.com/RADStudio/en/ e a página da unit System.Classes: https://docwiki.embarcadero.com/Libraries/en/System.Classes.
Imagem de Capa
Abaixo está uma imagem de capa gerada em SVG monocromática em tom pastel. Use-a como thumbnail para publicações.
Cor da Imagem: #C7E6A7 (pastel verde)
Referências e leituras recomendadas
Consulte a DocWiki da Embarcadero para referência de runtime e classes: https://docwiki.embarcadero.com/RADStudio/en/. Para padrões de projeto e Clean Code, consulte os livros correspondentes e adapte os princípios ao Object Pascal.
Conclusão
A POO em Delphi oferece recursos poderosos de modelagem quando usados com disciplina: declare métodos virtual na classe base, sobrescreva com override nas classes filhas, mantenha encapsulamento e siga regras de Clean Code. Os exemplos apresentados demonstram como projetar hierarquias seguras e testáveis, além de aplicar padrões simples que facilitam evolução do código. Pratique escrevendo pequenas hierarquias, adicionando testes unitários e revisando seu design conforme surgem novas responsabilidades.
