DMarlon Studio

Lección 5 · 5 de 20

Clases abstractas e interfaces · Dart

Defina contratos en Dart con clases abstractas e interfaces, y distinga extends de implements con varios métodos de pago.

Dart · Clases abstractas e interfaces en DMarlon Studio: el código del ejemplo y su salida en la terminal

Una clase `abstract` no se puede instanciar (PaymentMethod() no compila) y puede declarar miembros sin cuerpo: name y fee son abstractos, y cada subclase decide cómo son. charge, en cambio, es concreto: está escrito una vez y lo heredan todas, aunque dentro llame a fee, que la clase base todavía no sabe calcular.

Card y BankTransfer heredan con extends y rellenan lo que falta. La tarjeta cobra un 1,5 % más 25 céntimos, con división entera (~/); la transferencia no cobra comisión. Observe que name puede implementarse con un getter (get name =>) o con un campo final: para quien lo usa es lo mismo.

`implements` es otra cosa: toma solo el contrato, sin heredar código. Refundable se declara como abstract interface class, un modificador de Dart 3 que indica que está pensada para implementarse. Además, en Dart toda clase es una interfaz implícita: GiftCard implements PaymentMethod y por eso debe escribir todos sus miembros, también charge, que no recibe de nadie.

En el bucle, la variable es de tipo PaymentMethod y is Refundable pregunta si el objeto cumple también el otro contrato. Como Refundable no es un subtipo de PaymentMethod, Dart no cambia el tipo de la variable tras el is, y hay que convertirla con as Refundable para llamar a refund. La transferencia no es reembolsable y por eso no muestra esa línea.

Código · main.dart

// Abstract: cannot be instantiated, may leave methods without a body.abstract class PaymentMethod {  String get name;  int fee(int amount); // abstract: each subclass decides  // Concrete method that every subclass inherits.  String charge(int amount) => '$name: ${amount + fee(amount)} (fee ${fee(amount)})';}// A pure contract: whoever implements it must write every member.abstract interface class Refundable { String refund(int amount); }class Card extends PaymentMethod implements Refundable {  @override String get name => 'Card';  @override int fee(int amount) => amount * 15 ~/ 1000 + 25; // 1.5 % + 25  @override String refund(int amount) => 'refunds $amount to the card';}class BankTransfer extends PaymentMethod {  @override final String name = 'Transfer';  @override int fee(int amount) => 0;}// implements takes only the contract: nothing is inherited.class GiftCard implements PaymentMethod, Refundable {  @override String get name => 'Gift card';  @override int fee(int amount) => 0;  @override String charge(int amount) => '$name: $amount from the balance';  @override String refund(int amount) => 'adds $amount back to the gift card';}void main() {  for (final PaymentMethod method in [Card(), BankTransfer(), GiftCard()]) {    print(method.charge(4000));    if (method is Refundable) print('  ${(method as Refundable).refund(4000)}');  }}

Salida · dart run main.dart

Card: 4085 (fee 85)
  refunds 4000 to the card
Transfer: 4000 (fee 0)
Gift card: 4000 from the balance
  adds 4000 back to the gift card

Para recordar

  • Una clase abstract no se instancia y puede dejar métodos sin cuerpo.
  • extends hereda código; implements solo obliga a cumplir el contrato.
  • Toda clase de Dart sirve también como interfaz.
  • Una clase puede implementar varias interfaces a la vez.
  • is comprueba si un objeto cumple una interfaz.

Ejercicio

Cree un método de pago MobileWallet que implemente PaymentMethod y Refundable, con una comisión fija de 10 céntimos, y añádalo a la lista.