# DRY y KISS - Principios de Simplicidad
## DRY - Don't Repeat Yourself
### No Te Repitas
> "Cada pieza de conocimiento debe tener una representación única, sin ambigüedades y autoritativa dentro del
sistema"
createUser(String name, String email) async {
// Validación repetida
if (!email.contains('@')) {
throw ValidationException('Invalid email');
}
if (name.length < 2) { throw ValidationException('Name too short'); } return repository.create(User(name:
name, email: email)); } Future updateUser(String id, String name, String email) async {
// ❌ Misma validación repetida
if (!email.contains('@')) {
throw ValidationException('Invalid email');
}
if (name.length < 2) { throw ValidationException('Name too short'); } return repository.update(id,
User(name: name, email: email)); } } ``` #### ✅ Con DRY ```dart class UserValidator { static void
validateEmail(String email) { if (!email.contains('@')) { throw ValidationException('Invalid email'); } }
static void validateName(String name) { if (name.length < 2) { throw ValidationException('Name too
short'); } } static void validateUser(String name, String email) { validateName(name);
validateEmail(email); } } class UserService { Future createUser(String name, String email) async {
UserValidator.validateUser(name, email); // ✅ Reutilizado
return repository.create(User(name: name, email: email));
}
Future updateUser(String id, String name, String email) async {
UserValidator.validateUser(name, email); // ✅ Reutilizado
return repository.update(id, User(name: name, email: email));
}
}
```
### DRY con Extensiones
```dart
// Crear extensiones para código repetido
extension StringExtensions on String {
bool get isValidEmail {
final emailRegex = RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$');
return emailRegex.hasMatch(this);
}
String capitalize() {
if (isEmpty) return this;
return '${this[0].toUpperCase()}${substring(1)}';
}
}
extension DateTimeExtensions on DateTime {
String get formattedDate => '${day.toString().padLeft(2, '0')}/${month.toString().padLeft(2,
'0')}/$year';
bool get isToday {
final now = DateTime.now();
return year == now.year && month == now.month && day == now.day;
}
}
// Uso
final email = '[email protected]';
if (email.isValidEmail) {
print('Valid email');
}
final name = 'john';
print(name.capitalize()); // John
final date = DateTime.now();
print(date.formattedDate); // 28/11/2025
```
## KISS - Keep It Simple, Stupid
### Mantenlo Simple
> "La simplicidad debe ser un objetivo clave en el diseño, y la complejidad innecesaria debe evitarse"
### ❌ Código Complicado
```dart
class ComplexUserValidator {
bool validateUser(Map userData) {
// ❌ Demasiado complejo
return ((userData['email'] as String?)?.isNotEmpty ?? false) &&
((userData['email'] as String?)?.contains('@') ?? false) &&
((userData['email'] as String?)?.split('@').length == 2) &&
((userData['email'] as String?)?.split('@')[1].contains('.') ?? false) &&
((userData['name'] as String?)?.isNotEmpty ?? false) &&
((userData['name'] as String?)?.length ?? 0) >= 2 &&
((userData['age'] as int?) != null) &&
((userData['age'] as int?) ?? 0) >= 18 &&
((userData['age'] as int?) ?? 0) <= 120; } } ``` ### ✅ Código Simple ```dart class User { final String
email; final String name; final int age; User({required this.email, required this.name, required
this.age}); // Validación simple y clara bool get hasValidEmail { final parts=email.split('@');
return parts.length==2 && parts[1].contains('.'); } bool get hasValidName=> name.length >= 2;
bool get hasValidAge => age >= 18 && age <= 120; bool get isValid=> hasValidEmail && hasValidName &&
hasValidAge;
}
// Uso simple
final user = User(email: '[email protected]', name: 'John', age: 25);
if (user.isValid) {
print('User is valid');
}
```
### KISS en Arquitectura
#### ❌ Sobre-Ingeniería
```dart
// ❌ Demasiadas capas para una app simple
abstract class GetUserUseCaseInterface {
Future> execute(GetUserParams params);
}
class GetUserParams {
final String userId;
GetUserParams(this.userId);
}
class GetUserUseCaseImpl implements GetUserUseCaseInterface {
final UserRepositoryInterface repository;
@override
Future> execute(GetUserParams params) {
return repository.getUser(params.userId);
}
}
// Para una app simple, esto es excesivo
```
#### ✅ Simple y Suficiente
```dart
// ✅ Para apps simples, esto puede ser suficiente
class UserService {
final http.Client client;
UserService(this.client);
Future getUser(String id) async {
final response = await client.get(Uri.parse('/users/$id'));
if (response.statusCode == 200) {
return User.fromJson(json.decode(response.body));
}
throw Exception('Failed to load user');
}
}
```
### KISS en Widgets
#### ❌ Widget Complejo
```dart
class ComplexButton extends StatelessWidget {
final VoidCallback? onPressed;
final String text;
final Color? backgroundColor;
final Color? textColor;
final double? fontSize;
final FontWeight? fontWeight;
final EdgeInsets? padding;
final BorderRadius? borderRadius;
final double? elevation;
final BoxShadow? shadow;
final Gradient? gradient;
// ... 20 parámetros más
// Constructor gigante
const ComplexButton({
required this.onPressed,
required this.text,
this.backgroundColor,
this.textColor,
this.fontSize,
this.fontWeight,
this.padding,
this.borderRadius,
this.elevation,
this.shadow,
this.gradient,
// ...
});
@override
Widget build(BuildContext context) {
// 200 líneas de lógica compleja
return Container(/* ... */);
}
}
```
#### ✅ Widget Simple
```dart
// ✅ Simple con valores predeterminados sensatos
class SimpleButton extends StatelessWidget {
final VoidCallback? onPressed;
final String text;
final ButtonStyle? style; // Permite personalización sin explotar parámetros
const SimpleButton({
required this.onPressed,
required this.text,
this.style,
});
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: onPressed,
style: style,
child: Text(text),
);
}
}
// Para casos especiales, usa el ElevatedButton directamente
```
## Equilibrio entre DRY y KISS
### Cuando NO aplicar DRY
```dart
// A veces, un poco de repetición es mejor que abstracción prematura
// ❌ Abstracción prematura
class ConfigurableValidator {
final List> rules;
final T Function(Map) parser;
// ... código muy genérico y complejo
}
// ✅ Simple y directo (aunque se repita un poco)
class EmailValidator {
bool isValid(String email) => email.contains('@');
}
class PasswordValidator {
bool isValid(String password) => password.length >= 8;
}
```
## Mejores Prácticas
### ✅ Hacer
1. **Extraer funciones/widgets reutilizables**
2. **Usar constantes para valores repetidos**
3. **Mantener funciones pequeñas y enfocadas**
4. **Preferir claridad sobre brevedad**
5. **Empezar simple, refactorizar cuando sea necesario**
```dart
// Constantes para valores repetidos
class AppConstants {
static const defaultPadding = 16.0;
static const borderRadius = 12.0;
static const animationDuration = Duration(milliseconds: 300);
}
// Funciones pequeñas y enfocadas
bool isValidEmail(String email) => email.contains('@') && email.contains('.');
bool isValidPassword(String password) => password.length >= 8;
bool isAdult(int age) => age >= 18;
```
### ❌ Evitar
1. **Abstracciones prematuras**
2. **Código "clever" difícil de leer**
3. **Sobre-ingeniería de soluciones simples**
4. **Copiar-pegar código sin entenderlo**
```dart
// ❌ Código "clever" difícil de leer
final result = list.fold