Jenner Acosta Diaz
Dart Website

Clases abstractas y enumeraciones

Clases abstractas:
En Dart, una clase abstracta es una clase que no puede ser instanciada directamente, sino que se utiliza como una clase base para otras clases derivadas. Una clase abstracta puede contener métodos abstractos, que son métodos sin implementación, así como métodos concretos con implementación. Las clases que heredan de una clase abstracta deben proporcionar implementaciones para los métodos abstractos definidos en la clase base.

Para definir una clase abstracta en Dart, se utiliza la palabra clave abstract antes de la palabra clave class.

Ejemplo:

abstract class Animal {
  void hacerSonido(); // Método abstracto sin implementación

  void comer() {
    print('El animal está comiendo.');
  } // Método concreto con implementación
}

class Perro extends Animal {
  @override
  void hacerSonido() {
    print('El perro ladra.');
  }
}

void main() {
  Perro miPerro = Perro();
  miPerro.hacerSonido(); // Imprime "El perro ladra."
  miPerro.comer(); // Imprime "El animal está comiendo."
}

En el ejemplo anterior, la clase Animal es una clase abstracta con un método abstracto hacerSonido() que no tiene implementación. La clase también tiene un método concreto comer() que tiene una implementación.

La clase Perro hereda de la clase Animal y proporciona una implementación para el método abstracto hacerSonido() utilizando la anotación @override.

Las clases abstractas son útiles cuando se quiere definir una estructura común y métodos comunes para clases relacionadas, pero no se desea instanciar directamente la clase base. Se utilizan como una herramienta de abstracción para compartir comportamiento y definir una interfaz común para las clases derivadas.

Enumeraciones:

En Dart, las enumeraciones (enums) son una forma de representar un conjunto fijo de valores constantes. Una enumeración permite definir un tipo con un conjunto predefinido de valores que se pueden utilizar en el código. Cada valor en una enumeración se denomina miembro o constante de la enumeración.

Para definir una enumeración en Dart, se utiliza la palabra clave enum.

Ejemplo:

enum DiaSemana {
  lunes,
  martes,
  miércoles,
  jueves,
  viernes,
  sábado,
  domingo
}

void main() {
  DiaSemana hoy = DiaSemana.miércoles;
  print(hoy); // Imprime "DiaSemana.miércoles"
}

En el ejemplo anterior, se define una enumeración llamada DiaSemana que representa los días de la semana. Los miembros de la enumeración son lunes, martes, miércoles, jueves, viernes, sábado y domingo.

En la función main(), se crea una variable hoy de tipo DiaSemana y se le asigna el valor DiaSemana.miércoles. Luego, se imprime el valor de hoy por consola.

Las enumeraciones son útiles cuando se desea representar un conjunto limitado y predefinido de valores en el código. Proporcionan una forma legible y mantenible de trabajar con valores constantes y ayudan a evitar errores al proporcionar un conjunto fijo de opciones válidas.

Extends

En Dart, las palabras clave extends e implements se utilizan para establecer relaciones de herencia y de implementación de interfaces entre clases.

La palabra clave extends se utiliza para establecer una relación de herencia entre una clase (subclase) y otra clase (superclase). La subclase hereda los miembros (propiedades y métodos) de la superclase, lo que significa que puede acceder y utilizar esos miembros en la subclase. Ejemplo:

   class Vehiculo {
     void acelerar() {
       print('El vehículo está acelerando.');
     }
   }

   class Coche extends Vehiculo {
     void frenar() {
       print('El coche está frenando.');
     }
   }

   void main() {
     Coche miCoche = Coche();
     miCoche.acelerar(); // Método heredado de la superclase
     miCoche.frenar(); // Método propio de la subclase
   }

En el ejemplo anterior, la clase Vehiculo es la superclase y la clase Coche es la subclase. La subclase Coche utiliza la palabra clave extends seguida del nombre de la superclase Vehiculo para establecer la relación de herencia.

Como resultado, la subclase Coche hereda el método acelerar() de la superclase Vehiculo y también puede definir sus propios métodos, como el método frenar().

Implements

La palabra clave implements se utiliza para establecer una relación de implementación de interfaces. Una interfaz en Dart define una lista de métodos que una clase debe implementar. Una clase que implementa una interfaz debe proporcionar implementaciones para todos los métodos definidos en la interfaz. Ejemplo:

   abstract class Animal {
     void hacerSonido();
   }

   class Perro implements Animal {
     @override
     void hacerSonido() {
       print('El perro ladra.');
     }
   }

   void main() {
     Perro miPerro = Perro();
     miPerro.hacerSonido(); // Método implementado de la interfaz
   }

En el ejemplo anterior, la clase Animal es una interfaz que define un método hacerSonido(). La clase Perro utiliza la palabra clave implements seguida del nombre de la interfaz Animal para establecer la relación de implementación.

La clase Perro debe proporcionar una implementación para el método hacerSonido() definido en la interfaz Animal, utilizando la anotación @override para indicar que está sobrescribiendo el método.

Al implementar la interfaz, la clase Perro debe proporcionar una implementación para el método hacerSonido(), en este caso, el perro ladra.

Las palabras clave extends e implements son fundamentales para establecer relaciones de herencia y de implementación de interfaces en Dart. Permiten crear jerarquías de clases y garantizar que las clases derivadas hereden comportamientos o cumplan con los contratos de las interfaces definidas en Dart.

Ejercicio de la Sesión

void main(){
  
  final windPlant = WindPlant( initialEnergy: 100);
  final nuclearPlant = NuclearPlant( energyLeft: 1000 );
  
  print( 'wind: ${ chargePhone(windPlant) }' );
  print( 'nuclear: ${ chargePhone(nuclearPlant) }' );
}

double chargePhone( EnergyPlant plant ){
  if( plant.energyLeft < 10 ){
    throw Exception('Not enough energy');
  }
  
  return plant.energyLeft - 10;
}

enum PlantType { nuclear, wind, water }

abstract class EnergyPlant {
  
  double energyLeft;
  final PlantType type; //nuclear, wind, water
  
  EnergyPlant({ 
    required this. energyLeft,
    required this.type
  });
  
  void consumeEnergy( double amount );
  
}

// extends o implements
class WindPlant extends EnergyPlant {
  
  WindPlant({ required double initialEnergy })
    : super( energyLeft: initialEnergy, type: PlantType.wind );
  
  @override
  void consumeEnergy( double amount ){
    energyLeft -= amount;
  }
}

class NuclearPlant implements EnergyPlant {
  
  @override
  double energyLeft;
  
  @override
  final PlantType type = PlantType.nuclear;
  
  NuclearPlant({ required this.energyLeft });
  
  @override
  void consumeEnergy( double amount ){
    energyLeft -= (amount * 0.5);
  }
  
}