Комментарии (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();
}
Моя текущая философия
Интерфейсы - это не просто для абстракции, это контракты:
- Contract with future code - описываю, как другой разработчик будет использовать мой код
- Contract with tests - даю возможность заменять реализацию на mock
- Contract with architecture - разделяю concerns и делаю систему расширяемой
Правило: Если в коде есть новый класс, который зависит от других - он, скорее всего, должен быть за интерфейсом.
Это не значит, что КАЖДЫЙ класс нужен интерфейс - но 60-70% моих классов сервисов, репозиториев и стратегий - всегда имеют интерфейсы.