# Clases Abstractas en Dart
Las clases abstractas son la base para crear jerarquías de tipos sólidas y contratos claros en tu código Dart.
## ¿Qué es una Clase Abstracta?
Una clase abstracta es una clase que no puede ser instanciada directamente. Sirve como plantilla para otras clases.
- Puede contener métodos implementados (con cuerpo).
- Puede contener métodos abstractos (sin cuerpo, solo firma).
- Se define usando la palabra clave `abstract`.
## Sintaxis Básica
```dart
abstract class Animal {
// Propiedad concreta
String name;
Animal(this.name);
// Método abstracto (debe ser implementado por hijos)
void makeSound();
// Método concreto (heredado por hijos)
void sleep() {
print('$name is sleeping');
}
}
// ❌ Error: No se puede instanciar
// final animal = Animal('Generic');
class Dog extends Animal {
Dog(String name) : super(name);
@override
void makeSound() {
print('Woof!');
}
}
class Cat extends Animal {
Cat(String name) : super(name);
@override
void makeSound() {
print('Meow!');
}
}
void main() {
final dog = Dog('Buddy');
dog.makeSound(); // Woof!
dog.sleep(); // Buddy is sleeping
}
```
## Casos de Uso en Flutter
### 1. Definición de Repositorios (Clean Architecture)
Las clases abstractas son perfectas para definir contratos de repositorios, permitiendo múltiples implementaciones (producción, test, mock).
```dart
abstract class AuthRepository {
Future login(String email, String password);
Future logout();
Stream get authStateChanges;
}
// Implementación con Firebase
class FirebaseAuthRepository implements AuthRepository {
@override
Future login(String email, String password) async {
// Lógica de Firebase
}
// ...
}
// Implementación Mock para Tests
class MockAuthRepository implements AuthRepository {
@override
Future login(String email, String password) async {
return User(id: '1', email: email);
}
// ...
}
```
### 2. Base Widgets o ViewModels
Puedes crear clases base para compartir lógica común entre widgets o viewmodels.
```dart
abstract class BaseViewModel extends ChangeNotifier {
bool _isLoading = false;
String? _error;
bool get isLoading => _isLoading;
String? get error => _error;
void setLoading(bool value) {
_isLoading = value;
notifyListeners();
}
void setError(String? value) {
_error = value;
notifyListeners();
}
// Método abstracto que cada ViewModel debe implementar
Future init();
}
class HomeViewModel extends BaseViewModel {
@override
Future init() async {
setLoading(true);
try {
// Cargar datos
} catch (e) {
setError(e.toString());
} finally {
setLoading(false);
}
}
}
```
## Diferencia con Interfaces
En Dart, **todas las clases definen implícitamente una interfaz**. Sin embargo, las clases abstractas se usan cuando quieres proveer alguna implementación base o estado compartido.
| Característica | Clase Abstracta (`extends`) | Interfaz (`implements`) |
|----------------|-----------------------------|-------------------------|
| Estado (campos) | ✅ Sí | ❌ No (debes re-declararlos) |
| Constructores | ✅ Sí | ❌ No |
| Métodos concretos | ✅ Se heredan | ❌ Debes re-implementarlos |
| Herencia múltiple | ❌ No (solo una superclase) | ✅ Sí (múltiples interfaces) |
## Mejores Prácticas
1. **Usa clases abstractas para relaciones "es un" (is-a).**
- `Dog` *es un* `Animal`.
2. **Prefiere composición sobre herencia profunda.**
- Evita jerarquías de herencia de más de 2-3 niveles.
3. **Define contratos claros.**
- Usa métodos abstractos para forzar comportamiento en subclases.
## Ejemplo Avanzado: Shape System
```dart
abstract class Shape {
final Point position;
Shape(this.position);
double get area;
double get perimeter;
void draw() {
print('Drawing shape at $position');
}
}
class Circle extends Shape {
final double radius;
Circle(Point position, this.radius) : super(position);
@override
double get area => 3.14159 * radius * radius;
@override
double get perimeter => 2 * 3.14159 * radius;
}
class Rectangle extends Shape {
final double width;
final double height;
Rectangle(Point position, this.width, this.height) : super(position);
@override
double get area => width * height;
@override
double get perimeter => 2 * (width + height);
}
```
Este apartado profundiza en los conceptos clave, proporcionando ejemplos prácticos y mejores prácticas para su aplicación en proyectos reales.
Al comprender estos detalles, podrás diseñar soluciones más robustas, mantenibles y escalables en tus aplicaciones Flutter y Dart.