Skip to content
IRC-CodingIRC-Coding
Anti-PatronesGod InterfacesInterface SegregationSingle ResponsibilitySOLIDDesign PatternsOOPRefactoring

Anti-Patterns: God Interfaces e Interface Segregation

God Interfaces, Interface Segregation Principle y Single Responsibility. Ejemplos Java para diseño limpio de interfaces.

S

schutzgeist

11 min read
Anti-Patterns: God Interfaces e Interface Segregation

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

  1. S - Single Responsibility: Cada clase tiene una única responsabilidad
  2. O - Open/Closed: Abierto para extensión, cerrado para modificación
  3. L - Liskov Substitution: Las subclases pueden reemplazar a las clases base
  4. I - Interface Segregation: No crear God Interfaces
  5. D - Dependency Inversion: Dependencias basadas en abstracciones

Ejercicios típicos de examen

  1. Identifica God Interfaces
  2. Refactoriza God Interfaces
  3. Aplica Interface Segregation
  4. Diseña interfaces especializadas
  5. 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.

Volver al blog
Share:

Entradas relacionadas