Princípios SOLID no Flutter: Implementação Prática com Exemplos Reais
Introdução
Os princípios SOLID são um conjunto de boas práticas de programação que tornam o código mais organizado, flexível e fácil de manter. No Flutter, aplicá-los corretamente pode melhorar a arquitetura do projeto e facilitar a escalabilidade.
Neste artigo, vamos explorar cada um dos cinco princípios de SOLID com exemplos práticos em Dart/Flutter.
—
1. Single Responsibility Principle (SRP)
O princípio da responsabilidade única estabelece que cada classe deve ter um único propósito. No Flutter, isso significa separar lógica de negócios da UI.
Exemplo: Separando a Lógica de Requisição de API
Em um app que busca dados da API, ao invés de colocar a requisição diretamente na UI, criamos um serviço dedicado.
class UserService {
final Dio dio;
UserService(this.dio);
Future<List> fetchUsers() async {
final response = await dio.get('https://jsonplaceholder.typicode.com/users');
return (response.data as List).map((e) => User.fromJson(e)).toList();
}
}
A UI agora apenas exibe os dados:
class UserScreen extends StatelessWidget {
final UserService userService;
UserScreen({required this.userService});
@override
Widget build(BuildContext context) {
return FutureBuilder<List>(
future: userService.fetchUsers(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return CircularProgressIndicator();
}
return ListView.builder(
itemCount: snapshot.data?.length ?? 0,
itemBuilder: (context, index) => ListTile(
title: Text(snapshot.data![index].name),
),
);
},
);
}
}
—
2. Open/Closed Principle (OCP)
O código deve estar aberto para extensão, mas fechado para modificação. Isso significa que devemos usar abstrações para adicionar funcionalidades sem alterar código existente.
Exemplo: Criando um Sistema de Pagamento Flexível
abstract class PaymentProcessor {
void pay(double amount);
}
class CreditCardPayment implements PaymentProcessor {
@override
void pay(double amount) {
print('Pagando \$amount com cartão de crédito');
}
}
class PaypalPayment implements PaymentProcessor {
@override
void pay(double amount) {
print('Pagando \$amount com PayPal');
}
}
class PaymentService {
final PaymentProcessor processor;
PaymentService(this.processor);
void makePayment(double amount) {
processor.pay(amount);
}
}
Agora podemos adicionar novos métodos de pagamento sem modificar as classes existentes!
—
3. Liskov Substitution Principle (LSP)
O princípio de substituição de Liskov diz que subclasses devem ser substituíveis pelas superclasses sem alterar o comportamento do programa.
Exemplo: Criando Modelos de Notificação
abstract class NotificationSender {
void send(String message);
}
class EmailNotification implements NotificationSender {
@override
void send(String message) {
print('Enviando email: \$message');
}
}
class SMSNotification implements NotificationSender {
@override
void send(String message) {
print('Enviando SMS: \$message');
}
}
void notifyUser(NotificationSender sender, String message) {
sender.send(message);
}
void main() {
notifyUser(EmailNotification(), 'Bem-vindo ao Flutter!');
notifyUser(SMSNotification(), 'Seu código de verificação é 1234.');
}
—
4. Interface Segregation Principle (ISP)
O ISP afirma que uma classe não deve ser forçada a implementar métodos que não usa.
Exemplo: Interfaces para Diferentes Tipos de Relatórios
abstract class Report {
void generate();
}
abstract class PrintableReport {
void printReport();
}
class PDFReport implements Report, PrintableReport {
@override
void generate() {
print('Gerando PDF');
}
@override
void printReport() {
print('Imprimindo PDF');
}
}
class EmailReport implements Report {
@override
void generate() {
print('Gerando relatório para e-mail');
}
}
Agora cada classe implementa apenas os métodos necessários.
—
5. Dependency Inversion Principle (DIP)
O DIP sugere que módulos de alto nível não devem depender de módulos de baixo nível diretamente, mas sim de abstrações.
Exemplo: Injeção de Dependência para Banco de Dados
abstract class Database {
void save(String data);
}
class FirebaseDatabase implements Database {
@override
void save(String data) {
print('Salvando no Firebase: \$data');
}
}
class SQLDatabase implements Database {
@override
void save(String data) {
print('Salvando no SQL: \$data');
}
}
class UserRepository {
final Database database;
UserRepository(this.database);
void saveUser(String user) {
database.save(user);
}
}
Agora podemos trocar o banco de dados sem modificar o código do repositório!
—
Conclusão
Os princípios SOLID ajudam a criar um código mais estruturado, reutilizável e fácil de manter. No Flutter, aplicá-los corretamente pode evitar problemas no crescimento do projeto.
Publicar comentário