Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Абстрактный класс в Dart
Абстрактный класс — это класс, который нельзя инстанцировать напрямую. Он служит шаблоном (blueprint) для других классов, определяя интерфейс и базовую функциональность, которую должны реализовать классы-наследники.
Основные характеристики
Абстрактный класс:
- Объявляется с ключевым словом abstract
- Не может быть создан объект напрямую (
new AbstractClass()— ошибка) - Может содержать абстрактные методы (без реализации)
- Может содержать обычные методы с реализацией
- Может содержать переменные и конструкторы
- Используется для наследования
Синтаксис
// Определение абстрактного класса
abstract class Animal {
// Абстрактный метод (без реализации)
void makeSound();
// Обычный метод с реализацией
void sleep() {
print('Zzz...');
}
// Переменная
String name;
// Конструктор
Animal({required this.name});
}
// Класс-наследник должен реализовать все абстрактные методы
class Dog extends Animal {
Dog({required String name}) : super(name: name);
@override
void makeSound() {
print('Woof!');
}
}
class Cat extends Animal {
Cat({required String name}) : super(name: name);
@override
void makeSound() {
print('Meow!');
}
}
void main() {
// ❌ Нельзя инстанцировать абстрактный класс
// var animal = Animal(name: 'Generic'); // Ошибка!
// ✅ Инстанцируем наследников
var dog = Dog(name: 'Rex');
var cat = Cat(name: 'Whiskers');
dog.makeSound(); // Выведет: Woof!
cat.makeSound(); // Выведет: Meow!
dog.sleep(); // Выведет: Zzz...
cat.sleep(); // Выведет: Zzz...
}
Абстрактные методы
Абстрактный метод — это метод без реализации. Каждый класс-наследник обязан его реализовать.
abstract class Shape {
// Абстрактный метод — только сигнатура
double calculateArea();
// Абстрактный метод с параметрами
void draw(Canvas canvas);
// Метод с реализацией (опционально переопределять)
void printInfo() {
print('This is a shape');
}
}
class Circle extends Shape {
double radius;
Circle({required this.radius});
@override
double calculateArea() {
return 3.14 * radius * radius;
}
@override
void draw(Canvas canvas) {
// Рисовать круг
}
}
class Rectangle extends Shape {
double width, height;
Rectangle({required this.width, required this.height});
@override
double calculateArea() {
return width * height;
}
@override
void draw(Canvas canvas) {
// Рисовать прямоугольник
}
}
Абстрактный класс с полной реализацией
Абстрактный класс может содержать методы с полной реализацией:
abstract class Vehicle {
String brand;
Vehicle({required this.brand});
// Абстрактный метод
void drive();
// Конкретный метод
void honk() {
print('$brand: Honk honk!');
}
void showBrand() {
print('Brand: $brand');
}
}
class Car extends Vehicle {
Car({required String brand}) : super(brand: brand);
@override
void drive() {
print('$brand car is driving on road');
}
}
class Boat extends Vehicle {
Boat({required String brand}) : super(brand: brand);
@override
void drive() {
print('$brand boat is sailing on water');
}
}
void main() {
var car = Car(brand: 'Toyota');
var boat = Boat(brand: 'Yamaha');
car.drive(); // Toyota car is driving on road
car.honk(); // Toyota: Honk honk!
car.showBrand(); // Brand: Toyota
boat.drive(); // Yamaha boat is sailing on water
boat.honk(); // Yamaha: Honk honk!
}
Различие с interface
В Dart нет отдельного ключевого слова interface. Вместо этого используются классы (обычно абстрактные) или abstract class.
Абстрактный класс vs обычный класс:
// Абстрактный класс
abstract class Logger {
void log(String message); // Абстрактный метод
void info(String msg) {
log('[INFO] $msg');
}
}
// Реализация
class ConsoleLogger extends Logger {
@override
void log(String message) {
print(message);
}
}
// Использование как интерфейс
class MyApp {
final Logger _logger;
MyApp(this._logger);
void run() {
_logger.info('App started');
}
}
void main() {
var logger = ConsoleLogger();
var app = MyApp(logger);
app.run(); // App started
}
Абстрактный класс с конструктором
abstract class DatabaseConnection {
String connectionString;
bool isConnected = false;
// Конструктор для инициализации
DatabaseConnection({required this.connectionString});
// Абстрактные методы
Future<void> connect();
Future<void> disconnect();
Future<List<Map>> query(String sql);
// Конкретный метод
void printStatus() {
print('Connected: $isConnected to $connectionString');
}
}
class PostgreSQLConnection extends DatabaseConnection {
PostgreSQLConnection({required String connectionString})
: super(connectionString: connectionString);
@override
Future<void> connect() async {
print('Connecting to PostgreSQL...');
await Future.delayed(Duration(milliseconds: 500));
isConnected = true;
}
@override
Future<void> disconnect() async {
isConnected = false;
}
@override
Future<List<Map>> query(String sql) async {
return [{"id": 1, "name": "John"}];
}
}
Практический пример: Archicture паттерн
// Domain Layer
abstract class UserRepository {
Future<User> getUserById(String id);
Future<void> saveUser(User user);
Future<void> deleteUser(String id);
}
// Infrastructure Layer
class UserRepositoryImpl implements UserRepository {
final ApiClient _apiClient;
UserRepositoryImpl(this._apiClient);
@override
Future<User> getUserById(String id) async {
final response = await _apiClient.get('/users/$id');
return User.fromJson(response);
}
@override
Future<void> saveUser(User user) async {
await _apiClient.post('/users', user.toJson());
}
@override
Future<void> deleteUser(String id) async {
await _apiClient.delete('/users/$id');
}
}
// Application Layer
class GetUserUseCase {
final UserRepository _repository;
GetUserUseCase(this._repository);
Future<User> call(String id) => _repository.getUserById(id);
}
void main() async {
final apiClient = ApiClient();
final repository = UserRepositoryImpl(apiClient);
final useCase = GetUserUseCase(repository);
final user = await useCase('123');
print(user.name);
}
Преимущества абстрактных классов
1. Контрактность — определяет, что должны реализовать наследники
2. Полиморфизм — можно использовать ссылку на абстрактный класс
List<Shape> shapes = [
Circle(radius: 5),
Rectangle(width: 10, height: 20),
];
for (var shape in shapes) {
print(shape.calculateArea()); // Полиморфизм
}
3. Code reusability — общий код в абстрактном классе
4. Design contracts — явно указывает, какие методы должны быть реализованы
5. Чистая архитектура — разделение слоёв (domain определяет interface, infrastructure реализует)
Вывод
Абстрактный класс — это мощный инструмент для проектирования архитектуры. Используется для определения контрактов, достижения полиморфизма и следования принципу DIP (Dependency Inversion Principle) из SOLID.