Classes Seladas
Parte da seção Programação Orientada a Objetos do Journey de C# da Coddy — lição 22 de 70.
Às vezes, você deseja interromper a cadeia de herança. A palavra-chave sealed impede que uma classe seja herdada, bloqueando sua implementação para que nenhuma outra classe possa estendê-la.
public sealed class FinalDog : Animal
{
public override void Speak()
{
Console.WriteLine("Woof!");
}
}
// Isso causaria um erro de compilação:
// public class SuperDog : FinalDog { }Por que você selaria uma classe? Segurança e integridade de design são motivos comuns. Quando você projetou cuidadosamente uma classe e não quer que ninguém altere seu comportamento por meio de herança, selá-la garante que sua implementação permaneça intacta.
Você também pode selar métodos individuais em vez de classes inteiras. Isso impede novas sobrescritas, permitindo ainda que a própria classe seja herdada:
public class Dog : Animal
{
public sealed override void Speak()
{
Console.WriteLine("Woof!");
}
}
public class Puppy : Dog
{
// Não é possível sobrescrever Speak() - está selado
// Mas pode adicionar novos métodos
public void Whimper()
{
Console.WriteLine("Whimper...");
}
}Observe que sealed em um método deve ser combinado com override. Você só pode selar um método que já era virtual em uma classe pai. Isso lhe dá um controle preciso: permita alguma customização na hierarquia de herança e, em seguida, bloqueie-a em um ponto específico.
Desafio
FácilVamos construir um sistema de processamento de pagamentos que demonstra como usar sealed para bloquear implementações críticas. Você criará uma hierarquia onde certos métodos de pagamento podem ser estendidos, mas os mais seguros são selados para evitar quaisquer modificações.
Você criará quatro arquivos para organizar seu código:
PaymentMethod.cs: Defina uma classePaymentMethodno namespacePayments. Esta classe base possui uma propriedadeName(string) e um construtor que a define. Inclua um métodovirtualchamadoProcessPayment(decimal amount)que retorna"Processing ${amount} via {Name}".CreditCardPayment.cs: Defina uma classeCreditCardPaymentno namespacePaymentsque herda dePaymentMethod. Adicione uma propriedadeCardNumber(string). O construtor aceita o nome e o número do cartão, usandobasepara passar o nome. SobrescrevaProcessPaymentcom a palavra-chavesealedpara que nenhuma classe derivada adicional possa alterar esse comportamento. Ele deve retornar"Charging ${amount} to card ending in {last 4 digits of CardNumber}".BankTransfer.cs: Defina uma classesealedchamadaBankTransferno namespacePaymentsque herda dePaymentMethod. Esta classe inteira é selada - ninguém pode herdar dela. Adicione uma propriedadeBankName(string). O construtor aceita o nome e o nome do banco. SobrescrevaProcessPaymentpara retornar"Transferring ${amount} from {BankName}".Program.cs: No seu arquivo principal, crie instâncias de todos os três tipos de pagamento usando valores de entrada. Armazene-os como referências dePaymentMethode chameProcessPaymentem cada um para demonstrar o polimorfismo com implementações seladas.
Você receberá quatro entradas:
- Número do cartão de crédito (16 dígitos)
- Nome do banco
- Valor do pagamento
Imprima o resultado do processamento de um pagamento através de cada método:
{base PaymentMethod result}
{CreditCardPayment result}
{BankTransfer result}Por exemplo, se as entradas forem 1234567890123456, First National, e 150.00, a saída deve ser:
Processing $150.00 via Generic
Charging $150.00 to card ending in 3456
Transferring $150.00 from First NationalUse "Generic" como o nome para a instância base de PaymentMethod, "Credit Card" para o pagamento com cartão de crédito e "Bank Transfer" para a transferência bancária.
O método selado em CreditCardPayment garante que, se alguém criar uma subclasse, não poderá alterar como os pagamentos com cartão de crédito são processados. A classe selada BankTransfer impede qualquer herança - ambos são padrões de segurança importantes em sistemas de pagamento!
Folha de consulta
A palavra-chave sealed impede que uma classe seja herdada ou que um método seja sobrescrito futuramente na cadeia de herança.
Selando uma classe:
public sealed class FinalDog : Animal
{
public override void Speak()
{
Console.WriteLine("Woof!");
}
}
// Isso causaria um erro de compilação:
// public class SuperDog : FinalDog { }Classes seladas são usadas para segurança e integridade de design, garantindo que a implementação não possa ser modificada através de herança.
Selando um método:
public class Dog : Animal
{
public sealed override void Speak()
{
Console.WriteLine("Woof!");
}
}
public class Puppy : Dog
{
// Não é possível sobrescrever Speak() - ele está selado
// Mas é possível adicionar novos métodos
public void Whimper()
{
Console.WriteLine("Whimper...");
}
}A palavra-chave sealed em um método deve ser combinada com override. Você só pode selar métodos que já eram virtual em uma classe pai. Isso permite a customização até um ponto específico na hierarquia e, então, a bloqueia.
Experimente você mesmo
using System;
using Payments;
class Program
{
public static void Main(string[] args)
{
// Ler as entradas
string cardNumber = Console.ReadLine();
string bankName = Console.ReadLine();
decimal amount = Convert.ToDecimal(Console.ReadLine());
// TODO: Crie uma instância base de PaymentMethod com o nome "Generic"
// Armazene-a como uma referência de PaymentMethod
// TODO: Crie uma instância de CreditCardPayment com o nome "Credit Card" e o número do cartão
// Armazene-a como uma referência de PaymentMethod
// TODO: Crie uma instância de BankTransfer com o nome "Bank Transfer" e o nome do banco
// Armazene-a como uma referência de PaymentMethod
// TODO: Chame ProcessPayment em cada um e imprima os resultados
// Cada resultado deve estar em sua própria linha
}
}
Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.
Todas as lições de Programação Orientada a Objetos
1Fundamentos de POO
Arquivos ExternosNamespaces e DiretivasIntrodução a Classes e ObjetosA Palavra-chave 'this'Métodos e ParâmetrosCampos vs PropriedadesConstrutoresInicializadores de ObjetosRecapitulação - Calculadora Simples4Herança
Sintaxe Básica de Herança (:)A Palavra-chave 'base'Palavras-chave Virtual e OverrideClasses SeladasA Classe Base 'object'Recapitulação - Hierarquia de Funcionários7Recursos Avançados
Sobrecarga de OperadoresIndexadores (this[])Sobrescrita de ToString()Métodos de ExtensãoRecapitulação - Lista Personalizada10Padrões de Projeto Parte 1
Introdução aos Padrões de ProjetoSingleton Thread-SafePadrão FactoryPadrão Observer (Eventos)Padrão Strategy2Propriedades e Membros Estáticos
Propriedades AutoimplementadasPropriedades de Apenas Leitura/EscritaCampos e Métodos EstáticosClasses EstáticasMembros com Corpo de Expressão5Polimorfismo e Interfaces
Polimorfismo: Compilação vs ExecuçãoInterface vs Classe AbstrataMúltiplas InterfacesInterfaces ExplícitasUpcasting e DowncastingRecapitulação - Calculadora de Formas8Conceitos Avançados de POO
Composição sobre HerançaGenerics (Classes e Métodos)Delegates e EventosAtributos e ReflectionIDisposable e a Instrução usingFundamentos de Injeção de Dependência11Padrões de Projeto Parte 2
Padrão CommandPadrão AdapterPadrão DecoratorPadrão Template MethodPadrão StatePadrão Composite3Arquitetura de Classes
Dados de Instância vs EstáticosPalavras-chave 'readonly' e 'const'Campos de SuporteRecapitulação - Gerenciador de Conta Bancária6Encapsulamento
Modificadores de AcessoPropriedades para EncapsulamentoImplementação de Ocultação de DadosPadrões de ImutabilidadeRecapitulação - Registros de Estudantes9Argumentos Variáveis
A Palavra-chave 'params'Parâmetros OpcionaisArgumentos NomeadosSobrecarga de Métodos