← Назад к вопросам

Когда последний раз писал интерфейс?

2.3 Middle🔥 171 комментариев
#Основы Java

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Практический опыт с интерфейсами: современный подход

Интерфейсы - это один из краеугольных камней качественной архитектуры Java. Последний раз я писал интерфейсы примерно неделю-две назад в текущем проекте, и это были ключевые элементы нашей системы.

Недавний пример: Repository Pattern

// Интерфейс - контракт для работы с данными
public interface UserRepository {
    User findById(String id);
    List<User> findByEmail(String email);
    void save(User user);
    void delete(String id);
}

// Реализация для PostgreSQL
public class UserRepositoryPostgres implements UserRepository {
    private JdbcTemplate jdbcTemplate;
    
    @Override
    public User findById(String id) {
        String sql = "SELECT * FROM users WHERE id = ?";
        return jdbcTemplate.queryForObject(sql, new UserRowMapper(), id);
    }
    
    @Override
    public void save(User user) {
        String sql = "INSERT INTO users (id, email, name) VALUES (?, ?, ?)";
        jdbcTemplate.update(sql, user.getId(), user.getEmail(), user.getName());
    }
}

// Реализация для тестов
public class UserRepositoryInMemory implements UserRepository {
    private Map<String, User> storage = new HashMap<>();
    
    @Override
    public User findById(String id) {
        return storage.get(id);
    }
    
    @Override
    public void save(User user) {
        storage.put(user.getId(), user);
    }
}

Почему интерфейсы здесь критичны:

  • Можно менять реализацию БД без изменения бизнес-логики
  • Тесты используют in-memory реализацию
  • Это основа Dependency Injection

Второй пример: Service Layer

// Service интерфейс - бизнес-сценарии
public interface OrderService {
    OrderDto createOrder(CreateOrderRequest request);
    OrderDto getOrder(String orderId);
    void cancelOrder(String orderId);
    List<OrderDto> getUserOrders(String userId);
}

public class OrderServiceImpl implements OrderService {
    private UserRepository userRepository;
    private OrderRepository orderRepository;
    private PaymentService paymentService;
    private NotificationPublisher notificationPublisher;
    
    @Override
    public OrderDto createOrder(CreateOrderRequest request) {
        // Валидация
        User user = userRepository.findById(request.getUserId());
        if (user == null) {
            throw new UserNotFoundException();
        }
        
        // Бизнес-логика
        Order order = new Order(request.getUserId(), request.getItems());
        orderRepository.save(order);
        
        // Сайд-эффекты (асинхронно)
        notificationPublisher.publish(new OrderCreatedEvent(order.getId()));
        
        return OrderDto.from(order);
    }
}

Третий пример: Callback интерфейсы для Stream API

// Функциональный интерфейс - функциональное программирование
@FunctionalInterface
public interface UserProcessor {
    void process(User user);
}

// Использование
public class UserBatchProcessor {
    private UserRepository repository;
    
    public void processAllUsers(UserProcessor processor) {
        List<User> users = repository.findAll();
        users.forEach(processor::process);
    }
}

// В клиенте
UserBatchProcessor processor = new UserBatchProcessor();
processor.processAllUsers(user -> {
    user.incrementLoginCount();
    repository.save(user);
});

Четвертый пример: Event-Driven Architecture

// Event интерфейс - основа Event Sourcing
public interface DomainEvent {
    long getAggregateId();
    LocalDateTime getOccurredAt();
    String getEventType();
}

// Конкретные события
public class UserCreatedEvent implements DomainEvent {
    private final long userId;
    private final String email;
    private final LocalDateTime occurredAt;
    
    @Override
    public long getAggregateId() {
        return userId;
    }
    
    @Override
    public String getEventType() {
        return "USER_CREATED";
    }
}

// Обработчики
public interface EventHandler<T extends DomainEvent> {
    void handle(T event);
}

public class UserCreatedEventHandler implements EventHandler<UserCreatedEvent> {
    private NotificationService notificationService;
    
    @Override
    public void handle(UserCreatedEvent event) {
        notificationService.sendWelcomeEmail(event.getEmail());
    }
}

Пятый пример: Strategy Pattern

// Интерфейс для разных стратегий расчета цены
public interface PricingStrategy {
    BigDecimal calculatePrice(Order order);
}

// Разные стратегии
public class RegularPricingStrategy implements PricingStrategy {
    @Override
    public BigDecimal calculatePrice(Order order) {
        return order.getBasePrice();
    }
}

public class VipPricingStrategy implements PricingStrategy {
    private static final BigDecimal VIP_DISCOUNT = new BigDecimal("0.9");
    
    @Override
    public BigDecimal calculatePrice(Order order) {
        return order.getBasePrice().multiply(VIP_DISCOUNT);
    }
}

// Использование
public class PriceCalculator {
    private PricingStrategy strategy;
    
    public PriceCalculator(User user) {
        this.strategy = user.isVip() 
            ? new VipPricingStrategy() 
            : new RegularPricingStrategy();
    }
    
    public BigDecimal calculate(Order order) {
        return strategy.calculatePrice(order);
    }
}

Когда я писал интерфейсы в последний раз (подробнее)

Вчера:

  • Рефакторил сервис авторизации, вынес логику за интерфейс
  • Добавил mock реализацию для тестов

На этой неделе:

  • Создал интерфейсы для различных провайдеров оплаты (Stripe, PayPal)
  • Позволило добавить новый провайдер за час

В текущем спринте:

  • Интерфейсы для data access layer (Repository)
  • Абстракция для уведомлений (Email, SMS, Push)

Лучшие практики при написании интерфейсов

// ✅ Хорошо: специализированный интерфейс
public interface OrderRepository {
    Order findById(String id);
    void save(Order order);
}

// ❌ Плохо: слишком общий интерфейс
public interface Repository<T> {
    T findById(Object id);
    void save(T entity);
    void delete(Object id);
    List<T> findAll();
}

// ✅ Хорошо: Single Responsibility
public interface UserValidator {
    ValidationResult validate(User user);
}

// ❌ Плохо: Interface Segregation нарушен
public interface UserManager {
    void createUser(User user);
    void updateUser(User user);
    void deleteUser(String id);
    User findById(String id);
    List<User> findAll();
    void sendEmail(String userId, String message);
    void generateReport();
}

Моя текущая философия

Интерфейсы - это не просто для абстракции, это контракты:

  1. Contract with future code - описываю, как другой разработчик будет использовать мой код
  2. Contract with tests - даю возможность заменять реализацию на mock
  3. Contract with architecture - разделяю concerns и делаю систему расширяемой

Правило: Если в коде есть новый класс, который зависит от других - он, скорее всего, должен быть за интерфейсом.

Это не значит, что КАЖДЫЙ класс нужен интерфейс - но 60-70% моих классов сервисов, репозиториев и стратегий - всегда имеют интерфейсы.