Antipatrones: God Interfaces y Segregación de Interfaces
Los God Interfaces son un antipatrón muy común en la programación orientada a objetos. Aparecen cuando una interfaz asume demasiadas responsabilidades y viola el Interface Segregation Principle.
¿Qué son los God Interfaces?
Los God Interfaces son interfaces sobrecargadas que definen demasiados métodos y agrupan responsabilidades distintas y no relacionadas entre sí. Obligan a las clases que las implementan a proporcionar funcionalidad que realmente no necesitan.
Problemas de los God Interfaces
- Violación del Single Responsibility Principle
- Dependencias innecesarias
- Baja testabilidad
- Alto acoplamiento
- Difícil mantenimiento
Ejemplos de God Interfaces
Mal diseño: God Interface
// Anti-Pattern: God Interface
public interface UniversalSystemManager {
// Benutzer-Management
void createUser(String username, String password);
void deleteUser(String userId);
User getUser(String userId);
List<User> getAllUsers();
boolean authenticateUser(String username, String password);
// Datenbank-Management
void connectToDatabase(String url, String user, String password);
void executeQuery(String sql);
void closeConnection();
void backupDatabase();
void restoreDatabase(String backupFile);
// Dateisystem-Management
void createFile(String path, String content);
void deleteFile(String path);
File readFile(String path);
void createDirectory(String path);
// Netzwerk-Management
void sendEmail(String to, String subject, String body);
void makeHttpRequest(String url);
void downloadFile(String url, String localPath);
// Logging
void logInfo(String message);
void logError(String message);
void logWarning(String message);
// Konfiguration
void setProperty(String key, String value);
String getProperty(String key);
Map<String, String> getAllProperties();
// Sicherheit
void encryptFile(String filePath);
void decryptFile(String filePath);
String generateHash(String input);
}
Problemas en la implementación
// Problem: Klasse muss alles implementieren, auch was nicht benötigt wird
public class SimpleUserManager implements UniversalSystemManager {
@Override
public void createUser(String username, String password) {
// Eigentliche Funktionalität
System.out.println("Creating user: " + username);
}
@Override
public void deleteUser(String userId) {
System.out.println("Deleting user: " + userId);
}
@Override
public User getUser(String userId) {
return new User(userId, "user" + userId);
}
@Override
public List<User> getAllUsers() {
return Arrays.asList(new User("1", "Alice"), new User("2", "Bob"));
}
@Override
public boolean authenticateUser(String username, String password) {
return "admin".equals(username) && "password".equals(password);
}
// Unnötige Implementierungen - werden nicht benötigt!
@Override
public void connectToDatabase(String url, String user, String password) {
throw new UnsupportedOperationException("Not supported by SimpleUserManager");
}
@Override
public void executeQuery(String sql) {
throw new UnsupportedOperationException("Not supported by SimpleUserManager");
}
@Override
public void closeConnection() {
throw new UnsupportedOperationException("Not supported by SimpleUserManager");
}
@Override
public void backupDatabase() {
throw new UnsupportedOperationException("Not supported by SimpleUserManager");
}
@Override
public void restoreDatabase(String backupFile) {
throw new UnsupportedOperationException("Not supported by SimpleUserManager");
}
@Override
public void createFile(String path, String content) {
throw new UnsupportedOperationException("Not supported by SimpleUserManager");
}
@Override
public void deleteFile(String path) {
throw new UnsupportedOperationException("Not supported by SimpleUserManager");
}
@Override
public File readFile(String path) {
throw new UnsupportedOperationException("Not supported by SimpleUserManager");
}
@Override
public void createDirectory(String path) {
throw new UnsupportedOperationException("Not supported by SimpleUserManager");
}
@Override
public void sendEmail(String to, String subject, String body) {
throw new UnsupportedOperationException("Not supported by SimpleUserManager");
}
@Override
public void makeHttpRequest(String url) {
throw new UnsupportedOperationException("Not supported by SimpleUserManager");
}
@Override
public void downloadFile(String url, String localPath) {
throw new UnsupportedOperationException("Not supported by SimpleUserManager");
}
@Override
public void logInfo(String message) {
System.out.println("INFO: " + message);
}
@Override
public void logError(String message) {
System.err.println("ERROR: " + message);
}
@Override
public void logWarning(String message) {
System.out.println("WARNING: " + message);
}
@Override
public void setProperty(String key, String value) {
throw new UnsupportedOperationException("Not supported by SimpleUserManager");
}
@Override
public String getProperty(String key) {
throw new UnsupportedOperationException("Not supported by SimpleUserManager");
}
@Override
public Map<String, String> getAllProperties() {
throw new UnsupportedOperationException("Not supported by SimpleUserManager");
}
@Override
public void encryptFile(String filePath) {
throw new UnsupportedOperationException("Not supported by SimpleUserManager");
}
@Override
public void decryptFile(String filePath) {
throw new UnsupportedOperationException("Not supported by SimpleUserManager");
}
@Override
public String generateHash(String input) {
throw new UnsupportedOperationException("Not supported by SimpleUserManager");
}
}
Interface Segregation Principle (ISP)
Buen diseño: Interfaces segregadas
// Gutes Design: Spezialisierte Interfaces nach Verantwortlichkeit
// 1. Benutzer-Management
public interface UserManager {
void createUser(String username, String password);
void deleteUser(String userId);
User getUser(String userId);
List<User> getAllUsers();
boolean authenticateUser(String username, String password);
}
// 2. Datenbank-Management
public interface DatabaseManager {
void connectToDatabase(String url, String user, String password);
void executeQuery(String sql);
void closeConnection();
void backupDatabase();
void restoreDatabase(String backupFile);
}
// 3. Dateisystem-Management
public interface FileSystemManager {
void createFile(String path, String content);
void deleteFile(String path);
File readFile(String path);
void createDirectory(String path);
}
// 4. Netzwerk-Management
public interface NetworkManager {
void sendEmail(String to, String subject, String body);
void makeHttpRequest(String url);
void downloadFile(String url, String localPath);
}
// 5. Logging
public interface Logger {
void logInfo(String message);
void logError(String message);
void logWarning(String message);
}
// 6. Konfigurations-Management
public interface ConfigurationManager {
void setProperty(String key, String value);
String getProperty(String key);
Map<String, String> getAllProperties();
}
// 7. Sicherheits-Management
public interface SecurityManager {
void encryptFile(String filePath);
void decryptFile(String filePath);
String generateHash(String input);
}
Implementación con interfaces específicas
// Implementación solo de interfaces necesarias
public class SimpleUserManager implements UserManager, Logger {
private Map<String, User> users = new HashMap<>();
@Override
public void createUser(String username, String password) {
User user = new User(generateUserId(), username);
users.put(user.getId(), user);
logInfo("User created: " + username);
}
@Override
public void deleteUser(String userId) {
User user = users.remove(userId);
if (user != null) {
logInfo("User deleted: " + user.getUsername());
}
}
@Override
public User getUser(String userId) {
return users.get(userId);
}
@Override
public List<User> getAllUsers() {
return new ArrayList<>(users.values());
}
@Override
public boolean authenticateUser(String username, String password) {
return users.values().stream()
.anyMatch(user -> user.getUsername().equals(username) &&
user.getPassword().equals(password));
}
// Implementación de Logger
@Override
public void logInfo(String message) {
System.out.println("INFO: " + message);
}
@Override
public void logError(String message) {
System.err.println("ERROR: " + message);
}
@Override
public void logWarning(String message) {
System.out.println("WARNING: " + message);
}
private String generateUserId() {
return "user_" + System.currentTimeMillis();
}
}
// Implementación compleja con múltiples interfaces
public class EnterpriseSystemManager implements UserManager, DatabaseManager,
FileSystemManager, NetworkManager,
Logger, ConfigurationManager, SecurityManager {
private UserManager userManager;
private DatabaseManager databaseManager;
private FileSystemManager fileSystemManager;
private NetworkManager networkManager;
private Logger logger;
private ConfigurationManager configManager;
private SecurityManager securityManager;
public EnterpriseSystemManager() {
// Delegación a clases especializadas
this.userManager = new DatabaseBackedUserManager();
this.databaseManager = new PostgreSQLManager();
this.fileSystemManager = new LocalFileSystemManager();
this.networkManager = new HTTPClientManager();
this.logger = new FileLogger();
this.configManager = new PropertiesConfigManager();
this.securityManager = new AESecurityManager();
}
// Delegación a implementaciones especializadas
@Override
public void createUser(String username, String password) {
userManager.createUser(username, password);
}
@Override
public void deleteUser(String userId) {
userManager.deleteUser(userId);
}
@Override
public User getUser(String userId) {
return userManager.getUser(userId);
}
@Override
public List<User> getAllUsers() {
return userManager.getAllUsers();
}
@Override
public boolean authenticateUser(String username, String password) {
return userManager.authenticateUser(username, password);
}
@Override
public void connectToDatabase(String url, String user, String password) {
databaseManager.connectToDatabase(url, user, password);
}
@Override
public void executeQuery(String sql) {
databaseManager.executeQuery(sql);
}
@Override
public void closeConnection() {
databaseManager.closeConnection();
}
@Override
public void backupDatabase() {
databaseManager.backupDatabase();
}
@Override
public void restoreDatabase(String backupFile) {
databaseManager.restoreDatabase(backupFile);
}
// ... delegaciones adicionales
}
Refactorización de God Interfaces
Refactorización paso a paso
// Paso 1: Identificar responsabilidades
public class RefactoringExample {
// God Interface original
public interface GodService {
// Operaciones de usuario
User findUser(String id);
void saveUser(User user);
// Operaciones de producto
Product findProduct(String id);
List<Product> searchProducts(String query);
// Operaciones de pedido
Order createOrder(Order order);
void updateOrderStatus(String orderId, String status);
// Operaciones de pago
Payment processPayment(Payment payment);
void refundPayment(String paymentId);
// Operaciones de notificación
void sendEmail(String to, String subject, String body);
void sendSMS(String to, String message);
}
// Paso 2: Dividir interfaces por responsabilidad
public interface UserService {
User findUser(String id);
void saveUser(User user);
}
public interface ProductService {
Product findProduct(String id);
List<Product> searchProducts(String query);
}
public interface OrderService {
Order createOrder(Order order);
void updateOrderStatus(String orderId, String status);
}
public interface PaymentService {
Payment processPayment(Payment payment);
void refundPayment(String paymentId);
}
public interface NotificationService {
void sendEmail(String to, String subject, String body);
void sendSMS(String to, String message);
}
// Paso 3: Implementaciones especializadas
public class DatabaseUserService implements UserService {
private UserRepository userRepository;
@Override
public User findUser(String id) {
return userRepository.findById(id);
}
@Override
public void saveUser(User user) {
userRepository.save(user);
}
}
public class EmailNotificationService implements NotificationService {
private EmailSender emailSender;
private SMSSender smsSender;
@Override
public void sendEmail(String to, String subject, String body) {
emailSender.send(to, subject, body);
}
@Override
public void sendSMS(String to, String message) {
smsSender.send(to, message);
}
}
// Paso 4: Facade para facilitar el uso (opcional)
public class ECommerceFacade {
private UserService userService;
private ProductService productService;
private OrderService orderService;
private PaymentService paymentService;
private NotificationService notificationService;
public ECommerceFacade(UserService userService, ProductService productService,
OrderService orderService, PaymentService paymentService,
NotificationService notificationService) {
this.userService = userService;
this.productService = productService;
this.orderService = orderService;
this.paymentService = paymentService;
this.notificationService = notificationService;
}
// Operaciones de alto nivel
public Order placeOrder(String userId, String productId, PaymentDetails paymentDetails) {
User user = userService.findUser(userId);
Product product = productService.findProduct(productId);
Order order = orderService.createOrder(new Order(user, product));
Payment payment = paymentService.processPayment(
new Payment(order, paymentDetails)
);
if (payment.isSuccessful()) {
orderService.updateOrderStatus(order.getId(), "PAID");
notificationService.sendEmail(user.getEmail(),
"Order Confirmation", "Your order " + order.getId() + " is confirmed");
}
return order;
}
}
}
Mejores Prácticas en Diseño de Interfaces
1. Interfaces Basadas en Roles
// Interfaces basierend auf Rollen statt Implementierungen
public interface Readable {
String read();
}
public interface Writable {
void write(String content);
}
public interface Appendable {
void append(String content);
}
public interface Flushable {
void flush();
}
// Kombination von Interfaces
public interface FileOperations extends Readable, Writable, Appendable, Flushable {
String getPath();
long getSize();
}
public interface NetworkOperations extends Readable, Writable {
String getRemoteAddress();
int getPort();
}
2. Interfaces Marcadores
// Marker Interfaces für spezielle Eigenschaften
public interface Serializable {
}
public interface Cloneable {
}
public interface Remote {
}
// Eigene Marker Interfaces
public interface Auditable {
}
public interface Cacheable {
}
public interface Secure {
}
// Verwendung
public class Document implements Serializable, Auditable, Cacheable {
// Implementierung
}
3. Interfaces Funcionales
// Spezialisierte Functional Interfaces
@FunctionalInterface
public interface UserPredicate {
boolean test(User user);
}
@FunctionalInterface
public interface UserFunction<T> {
T apply(User user);
}
@FunctionalInterface
public interface UserConsumer {
void accept(User user);
}
// Verwendung
public class UserProcessor {
public List<User> filterUsers(List<User> users, UserPredicate predicate) {
return users.stream()
.filter(predicate::test)
.collect(Collectors.toList());
}
public void processUsers(List<User> users, UserConsumer consumer) {
users.forEach(consumer::accept);
}
}
Detección y Prevención de God Interfaces
Señales de Alerta para God Interfaces
public class GodInterfaceDetector {
// 1. Demasiados métodos en la interfaz
public boolean hasTooManyMethods(Class<?> interfaceClass) {
return interfaceClass.isInterface() &&
interfaceClass.getMethods().length > 10;
}
// 2. Múltiples responsabilidades
public boolean hasMultipleResponsibilities(Class<?> interfaceClass) {
if (!interfaceClass.isInterface()) return false;
Method[] methods = interfaceClass.getMethods();
Set<String> responsibilities = new HashSet<>();
for (Method method : methods) {
String responsibility = extractResponsibility(method);
responsibilities.add(responsibility);
}
return responsibilities.size() > 3;
}
private String extractResponsibility(Method method) {
String name = method.getName().toLowerCase();
if (name.contains("user") || name.contains("customer")) return "user-management";
if (name.contains("product") || name.contains("item")) return "product-management";
if (name.contains("order") || name.contains("sale")) return "order-management";
if (name.contains("payment") || name.contains("transaction")) return "payment-management";
if (name.contains("email") || name.contains("notification")) return "notification";
if (name.contains("database") || name.contains("query")) return "database";
if (name.contains("file") || name.contains("directory")) return "filesystem";
if (name.contains("log") || name.contains("error")) return "logging";
if (name.contains("config") || name.contains("property")) return "configuration";
if (name.contains("security") || name.contains("encrypt")) return "security";
return "other";
}
// 3. Implementaciones con muchas UnsupportedOperationException
public boolean hasManyUnsupportedOperations(Class<?> implementationClass) {
Method[] methods = implementationClass.getMethods();
long unsupportedCount = Arrays.stream(methods)
.filter(this::isUnsupportedOperation)
.count();
return unsupportedCount > methods.length * 0.3; // > 30% unsupported
}
private boolean isUnsupportedOperation(Method method) {
try {
if (method.getDeclaringClass().isInterface()) {
return method.getAnnotation(Deprecated.class) != null ||
method.getName().startsWith("not") ||
method.getName().contains("Unsupported");
}
} catch (Exception e) {
return false;
}
return false;
}
}
Estrategias de Refactorización
public class InterfaceRefactoringStrategies {
// 1. Extracción de Interfaces
public interface UserManagement {
void createUser(User user);
void updateUser(User user);
void deleteUser(String userId);
User getUser(String userId);
List<User> getAllUsers();
}
public interface Authentication {
boolean login(String username, String password);
void logout(String userId);
boolean isAuthenticated(String userId);
String getCurrentUser();
}
public interface Authorization {
boolean hasPermission(String userId, String permission);
void grantPermission(String userId, String permission);
void revokePermission(String userId, String permission);
List<String> getPermissions(String userId);
}
// 2. Segregación de Interfaces
public interface ReadOnlyRepository<T, ID> {
T findById(ID id);
List<T> findAll();
boolean existsById(ID id);
long count();
}
public interface WriteOnlyRepository<T, ID> {
<S extends T> S save(S entity);
<S extends T> Iterable<S> saveAll(Iterable<S> entities);
void deleteById(ID id);
void delete(T entity);
void deleteAll();
}
public interface Repository<T, ID> extends ReadOnlyRepository<T, ID>, WriteOnlyRepository<T, ID> {
}
// 3. Template Method Pattern para Interfaces
public interface DataAccessTemplate {
default <T> T executeInTransaction(Supplier<T> operation) {
beginTransaction();
try {
T result = operation.get();
commit();
return result;
} catch (Exception e) {
rollback();
throw new RuntimeException("Transaction failed", e);
}
}
void beginTransaction();
void commit();
void rollback();
}
}
Testing de Interfaces
Unit Tests para Diseño de Interfaces
public class InterfaceDesignTest {
@Test
public void testUserServiceInterface() {
UserService userService = new MockUserService();
// Testen, ob alle Methoden funktionieren
User user = new User("1", "test@example.com");
userService.saveUser(user);
User retrieved = userService.findUser("1");
assertNotNull(retrieved);
assertEquals("test@example.com", retrieved.getEmail());
}
@Test
public void testInterfaceSegregation() {
// Testen, dass Implementierungen nur benötigte Methoden haben
UserService userService = new SimpleUserService();
// Sollte funktionieren
assertDoesNotThrow(() -> userService.saveUser(new User("1", "test")));
// Sollte nicht existieren
assertFalse(hasMethod(userService, "sendEmail"));
}
private boolean hasMethod(Object obj, String methodName) {
return Arrays.stream(obj.getClass().getMethods())
.anyMatch(m -> m.getName().equals(methodName));
}
// Mock Implementation für Testing
private static class MockUserService implements UserService {
private Map<String, User> users = new HashMap<>();
@Override
public User findUser(String id) {
return users.get(id);
}
@Override
public void saveUser(User user) {
users.put(user.getId(), user);
}
@Override
public void updateUser(User user) {
users.put(user.getId(), user);
}
@Override
public void deleteUser(String userId) {
users.remove(userId);
}
@Override
public List<User> getAllUsers() {
return new ArrayList<>(users.values());
}
}
}
Conceptos evaluables en examen
Principios SOLID clave
- S - Single Responsibility: Cada clase tiene una única responsabilidad
- O - Open/Closed: Abierto para extensión, cerrado para modificación
- L - Liskov Substitution: Las subclases pueden reemplazar a las clases base
- I - Interface Segregation: No crear God Interfaces
- D - Dependency Inversion: Dependencias basadas en abstracciones
Ejercicios típicos de examen
- Identifica God Interfaces
- Refactoriza God Interfaces
- Aplica Interface Segregation
- Diseña interfaces especializadas
- Prueba diseños de interfaces
Resumen
Los God Interfaces son un anti-patrón peligroso:
- Problemas: Acoplamiento alto, dificultades para testear, dependencias innecesarias
- Solución: Aplicar Interface Segregation Principle
- Proceso: Identificar responsabilidades y dividirlas
- Resultado: Interfaces pequeñas y enfocadas con responsabilidades claras
Un buen diseño de interfaces es fundamental para arquitecturas de software mantenibles y testeables.
Lecturas recomendadas: Patrones de diseño
Keine Bücher für Kategorie "design-patterns" gefunden.

