Clases selladas (Sealed)
Parte de la sección Programación Orientada a Objetos del Journey de C# de Coddy — lección 22 de 70.
A veces querrás detener la cadena de herencia. La palabra clave sealed evita que una clase sea heredada, bloqueando su implementación para que ninguna otra clase pueda extenderla.
public sealed class FinalDog : Animal
{
public override void Speak()
{
Console.WriteLine("Woof!");
}
}
// Esto causaría un error de compilación:
// public class SuperDog : FinalDog { }¿Por qué sellarías una clase? La seguridad y la integridad del diseño son razones comunes. Cuando has diseñado cuidadosamente una clase y no quieres que nadie cambie su comportamiento a través de la herencia, sellarla garantiza que tu implementación permanezca intacta.
También puedes sellar métodos individuales en lugar de clases enteras. Esto evita que se sigan sobrescribiendo, permitiendo al mismo tiempo que la propia clase sea heredada:
public class Dog : Animal
{
public sealed override void Speak()
{
Console.WriteLine("Woof!");
}
}
public class Puppy : Dog
{
// No se puede sobrescribir Speak() - está sellado
// Pero se pueden agregar nuevos métodos
public void Whimper()
{
Console.WriteLine("Whimper...");
}
}Ten en cuenta que sealed en un método debe combinarse con override. Solo puedes sellar un método que ya era virtual en una clase base. Esto te brinda un control preciso: permite cierta personalización en la jerarquía de herencia y luego bloquéala en un punto específico.
Desafío
FácilVamos a construir un sistema de procesamiento de pagos que demuestre cómo usar sealed para bloquear implementaciones críticas. Crearás una jerarquía donde ciertos métodos de pago pueden extenderse, pero los más seguros están sellados para evitar cualquier modificación.
Crearás cuatro archivos para organizar tu código:
PaymentMethod.cs: Define una clasePaymentMethoden el espacio de nombresPayments. Esta clase base tiene una propiedadName(string) y un constructor que la establece. Incluye un métodovirtualllamadoProcessPayment(decimal amount)que devuelve"Processing ${amount} via {Name}".CreditCardPayment.cs: Define una claseCreditCardPaymenten el espacio de nombresPaymentsque hereda dePaymentMethod. Agrega una propiedadCardNumber(string). El constructor acepta el nombre y el número de tarjeta, usandobasepara pasar el nombre. SobrescribeProcessPaymentcon la palabra clavesealedpara que ninguna clase derivada posterior pueda cambiar este comportamiento. Debe devolver"Charging ${amount} to card ending in {last 4 digits of CardNumber}".BankTransfer.cs: Define una clasesealedllamadaBankTransferen el espacio de nombresPaymentsque hereda dePaymentMethod. Toda esta clase está sellada: nadie puede heredar de ella. Agrega una propiedadBankName(string). El constructor acepta el nombre y el nombre del banco. SobrescribeProcessPaymentpara devolver"Transferring ${amount} from {BankName}".Program.cs: En tu archivo principal, crea instancias de los tres tipos de pago utilizando los valores de entrada. Almacénalas como referencias dePaymentMethody llama aProcessPaymenten cada una para demostrar el polimorfismo con implementaciones selladas.
Recibirás cuatro entradas:
- Número de tarjeta de crédito (16 dígitos)
- Nombre del banco
- Monto del pago
Imprime el resultado de procesar un pago a través de cada método:
{base PaymentMethod result}
{CreditCardPayment result}
{BankTransfer result}Por ejemplo, si las entradas son 1234567890123456, First National, y 150.00, la salida debería ser:
Processing $150.00 via Generic
Charging $150.00 to card ending in 3456
Transferring $150.00 from First NationalUsa "Generic" como nombre para la instancia base de PaymentMethod, "Credit Card" para el pago con tarjeta de crédito y "Bank Transfer" para la transferencia bancaria.
El método sellado en CreditCardPayment asegura que si alguien crea una subclase, no pueda cambiar cómo se procesan los pagos con tarjeta de crédito. La clase sellada BankTransfer evita cualquier herencia en absoluto; ¡ambos son patrones de seguridad importantes en los sistemas de pago!
Hoja de referencia
La palabra clave sealed evita que una clase sea heredada o que un método sea sobrescrito más adelante en la cadena de herencia.
Sellar una clase:
public sealed class FinalDog : Animal
{
public override void Speak()
{
Console.WriteLine("Woof!");
}
}
// This would cause a compile error:
// public class SuperDog : FinalDog { }Las clases selladas se utilizan por seguridad e integridad del diseño, asegurando que la implementación no pueda ser modificada a través de la herencia.
Sellar un método:
public class Dog : Animal
{
public sealed override void Speak()
{
Console.WriteLine("Woof!");
}
}
public class Puppy : Dog
{
// Cannot override Speak() - it's sealed
// But can add new methods
public void Whimper()
{
Console.WriteLine("Whimper...");
}
}La palabra clave sealed en un método debe combinarse con override. Solo se pueden sellar métodos que ya eran virtual en una clase base. Esto permite la personalización hasta un punto específico de la jerarquía y luego la bloquea.
Pruébalo tú mismo
using System;
using Payments;
class Program
{
public static void Main(string[] args)
{
// Leer las entradas
string cardNumber = Console.ReadLine();
string bankName = Console.ReadLine();
decimal amount = Convert.ToDecimal(Console.ReadLine());
// TODO: Crear una instancia base de PaymentMethod con el nombre "Generic"
// Almacenarla como una referencia de PaymentMethod
// TODO: Crear una instancia de CreditCardPayment con el nombre "Credit Card" y el número de tarjeta
// Almacenarla como una referencia de PaymentMethod
// TODO: Crear una instancia de BankTransfer con el nombre "Bank Transfer" y el nombre del banco
// Almacenarla como una referencia de PaymentMethod
// TODO: Llamar a ProcessPayment en cada una e imprimir los resultados
// Cada resultado debe estar en su propia línea
}
}
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Programación Orientada a Objetos
1Fundamentos de OOP
Archivos externosNamespaces y directivasIntro a clases y objetosLa palabra clave 'this'Métodos y parámetrosCampos vs PropiedadesConstructoresInicializadores de objetosResumen - Calculadora simple4Herencia
Sintaxis básica de herencia (:)La palabra clave 'base'Palabras clave Virtual y OverrideClases selladas (Sealed)La clase base 'object'Resumen: Jerarquía de empleados7Características avanzadas
Sobrecarga de operadoresIndexadores (this[])Sobrescritura de ToString()Métodos de extensiónResumen - Lista personalizada10Patrones de diseño - Parte 1
Introducción a los patrones de diseñoSingleton Thread-SafePatrón FactoryPatrón Observer (Eventos)Patrón Strategy2Propiedades y miembros estáticos
Propiedades autoimplementadasPropiedades de solo lectura y escrituraCampos y métodos estáticosClases estáticasMiembros con cuerpo de expresión5Polimorfismo e Interfaces
Polimorfismo: Compilación vs. EjecuciónInterfaz vs. Clase AbstractaInterfaces MúltiplesInterfaces ExplícitasUpcasting y DowncastingRepaso: Calculadora de Figuras8Conceptos avanzados de POO
Composición sobre herenciaGenéricos (clases y métodos)Delegados y eventosAtributos y ReflexiónIDisposable y la sentencia usingFundamentos de Inyección de dependencias11Patrones de diseño, parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón Composite3Arquitectura de clases
Datos de instancia vs. estáticosPalabras clave 'readonly' y 'const'Campos de respaldoResumen - Gestor de cuentas bancarias6Encapsulamiento
Modificadores de accesoPropiedades para el encapsulamientoImplementación de ocultamiento de datosPatrones de inmutabilidadRepaso - Registros de estudiantes