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

С какими паттернами программирования работал

1.0 Junior🔥 121 комментариев
#SOLID и паттерны проектирования

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

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

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

Design Patterns: мой практический опыт

Я работал со множеством паттернов в production системах. Не буду перечислять все 23 из GoF книги — вместо этого расскажу о тех, которые реально спасают и используются постоянно.

1. Dependency Injection (DI)

Это не просто паттерн, это foundation современного Java:

// ❌ Плохо — жёсткое связывание
@RestController
public class OrderController {
    private EmailService emailService = new EmailService();
    private NotificationService notificationService = new NotificationService();
    
    // Сложно тестировать, нельзя менять реализацию
}

// ✅ Хорошо — инъекция зависимостей
@RestController
public class OrderController {
    private final EmailService emailService;
    private final NotificationService notificationService;
    
    // Constructor injection — явное, для тестирования
    public OrderController(
        EmailService emailService,
        NotificationService notificationService
    ) {
        this.emailService = emailService;
        this.notificationService = notificationService;
    }
    
    @PostMapping
    public ResponseEntity<Order> createOrder(@RequestBody OrderRequest request) {
        Order order = orderRepository.save(new Order(request));
        
        // Легко подменить в тестах
        emailService.sendConfirmation(order);
        return ResponseEntity.created(URI.create("/orders/" + order.getId())).build();
    }
}

// В тестах просто подменяем реализацию
@Test
void shouldSendEmailOnOrderCreation() {
    EmailService emailMock = mock(EmailService.class);
    NotificationService notificationMock = mock(NotificationService.class);
    OrderController controller = new OrderController(emailMock, notificationMock);
    
    // ... тест
}

2. Strategy Pattern

Полезен для выбора алгоритма runtime:

// Интерфейс стратегии
public interface PaymentStrategy {
    PaymentResult process(Payment payment);
}

// Конкретные стратегии
@Component
public class CreditCardPayment implements PaymentStrategy {
    @Override
    public PaymentResult process(Payment payment) {
        // Обработка платежа по карте
        validateCard(payment.getCard());
        return chargeCard(payment);
    }
}

@Component
public class PayPalPayment implements PaymentStrategy {
    @Override
    public PaymentResult process(Payment payment) {
        // Обработка платежа через PayPal
        authenticatePayPal(payment.getPaypalAccount());
        return initiatePayPalPayment(payment);
    }
}

// Контекст выбирает правильную стратегию
@Service
public class PaymentService {
    private final Map<PaymentMethod, PaymentStrategy> strategies;
    
    public PaymentService(
        CreditCardPayment creditCard,
        PayPalPayment paypal
    ) {
        this.strategies = Map.of(
            PaymentMethod.CREDIT_CARD, creditCard,
            PaymentMethod.PAYPAL, paypal
        );
    }
    
    public PaymentResult pay(Payment payment) {
        PaymentStrategy strategy = strategies.get(payment.getMethod());
        if (strategy == null) {
            throw new UnsupportedPaymentMethodException();
        }
        return strategy.process(payment);
    }
}

3. Repository Pattern

Абстракция для доступа к данным:

// Интерфейс репозитория
public interface UserRepository {
    Optional<User> findById(String id);
    List<User> findByEmail(String email);
    User save(User user);
    void delete(String id);
}

// Имплементация для PostgreSQL
@Repository
public class UserJpaRepository implements UserRepository {
    private final JpaUserRepository jpaRepo;
    
    @Override
    public Optional<User> findById(String id) {
        return jpaRepo.findById(id);
    }
    
    @Override
    public User save(User user) {
        return jpaRepo.save(user);
    }
}

// Можно подменить другой реализацией
@Repository
public class UserMemoryRepository implements UserRepository {
    private Map<String, User> storage = new ConcurrentHashMap<>();
    
    @Override
    public Optional<User> findById(String id) {
        return Optional.ofNullable(storage.get(id));
    }
}

// Сервис использует интерфейс, не зависит от реализации
@Service
public class UserService {
    private final UserRepository repository;
    
    public UserService(UserRepository repository) {
        this.repository = repository;
    }
}

4. Observer Pattern (Event-Driven)

Для асинхронной коммуникации между компонентами:

// Event
public class OrderCreatedEvent extends ApplicationEvent {
    private final Order order;
    
    public OrderCreatedEvent(Object source, Order order) {
        super(source);
        this.order = order;
    }
}

// Publisher
@Service
public class OrderService {
    private final ApplicationEventPublisher eventPublisher;
    
    public Order createOrder(OrderRequest request) {
        Order order = orderRepository.save(new Order(request));
        
        // Опубликовать событие — подписчики уведомляются
        eventPublisher.publishEvent(new OrderCreatedEvent(this, order));
        
        return order;
    }
}

// Observers (подписчики)
@Component
public class OrderEmailNotifier {
    @EventListener
    @Async
    public void onOrderCreated(OrderCreatedEvent event) {
        emailService.sendOrderConfirmation(event.getOrder());
    }
}

@Component
public class InventoryManager {
    @EventListener
    @Async
    public void onOrderCreated(OrderCreatedEvent event) {
        inventoryService.reserveItems(event.getOrder());
    }
}

5. Builder Pattern

Для создания сложных объектов:

// ❌ Антипаттерн — too many constructors
public class Order {
    public Order(String id) {...}
    public Order(String id, String customerId) {...}
    public Order(String id, String customerId, List<Item> items) {...}
    public Order(String id, String customerId, List<Item> items, 
                 LocalDateTime created) {...}
    // и ещё 10 конструкторов...
}

// ✅ Builder pattern
public class Order {
    private final String id;
    private final String customerId;
    private final List<Item> items;
    private final LocalDateTime createdAt;
    private final String status;
    
    private Order(Builder builder) {
        this.id = builder.id;
        this.customerId = builder.customerId;
        this.items = builder.items;
        this.createdAt = builder.createdAt;
        this.status = builder.status;
    }
    
    public static class Builder {
        private String id = UUID.randomUUID().toString();
        private String customerId;
        private List<Item> items = new ArrayList<>();
        private LocalDateTime createdAt = LocalDateTime.now();
        private String status = "PENDING";
        
        public Builder customerId(String customerId) {
            this.customerId = customerId;
            return this;
        }
        
        public Builder items(List<Item> items) {
            this.items = items;
            return this;
        }
        
        public Order build() {
            if (customerId == null) {
                throw new IllegalArgumentException("customerId is required");
            }
            return new Order(this);
        }
    }
}

// Использование
Order order = new Order.Builder()
    .customerId("user-123")
    .items(List.of(new Item("product-1", 2)))
    .build();

6. Factory Pattern

Для создания объектов без знания их типа:

public interface ReportGenerator {
    String generate(ReportData data);
}

public class PdfReportGenerator implements ReportGenerator { ... }
public class ExcelReportGenerator implements ReportGenerator { ... }
public class CsvReportGenerator implements ReportGenerator { ... }

// Factory
@Component
public class ReportGeneratorFactory {
    private final PdfReportGenerator pdfGenerator;
    private final ExcelReportGenerator excelGenerator;
    private final CsvReportGenerator csvGenerator;
    
    public ReportGenerator createGenerator(ReportFormat format) {
        return switch (format) {
            case PDF -> pdfGenerator;
            case EXCEL -> excelGenerator;
            case CSV -> csvGenerator;
            default -> throw new UnsupportedFormatException();
        };
    }
}

// Использование
@Service
public class ReportService {
    private final ReportGeneratorFactory factory;
    
    public byte[] generateReport(ReportData data, ReportFormat format) {
        ReportGenerator generator = factory.createGenerator(format);
        String content = generator.generate(data);
        return content.getBytes(StandardCharsets.UTF_8);
    }
}

7. Decorator Pattern

Для добавления функциональности динамически:

public interface DataSource {
    byte[] read();
}

public class FileDataSource implements DataSource {
    @Override
    public byte[] read() {
        return Files.readAllBytes(Paths.get("data.txt"));
    }
}

// Декоратор 1: Compression
public class CompressionDecorator implements DataSource {
    private final DataSource source;
    
    public CompressionDecorator(DataSource source) {
        this.source = source;
    }
    
    @Override
    public byte[] read() {
        return compress(source.read());
    }
}

// Декоратор 2: Encryption
public class EncryptionDecorator implements DataSource {
    private final DataSource source;
    
    public EncryptionDecorator(DataSource source) {
        this.source = source;
    }
    
    @Override
    public byte[] read() {
        return encrypt(source.read());
    }
}

// Комбинируем декораторы
DataSource data = new FileDataSource();
data = new CompressionDecorator(data);
data = new EncryptionDecorator(data);
byte[] result = data.read();  // Чтение -> сжатие -> шифрование

8. Adapter Pattern

Для интеграции несовместимых интерфейсов:

// Старый интерфейс платёжной системы
interface OldPaymentGateway {
    boolean charge(double amount, String cardNumber);
}

// Новый интерфейс, который мы хотим
public interface PaymentProcessor {
    PaymentResult process(Payment payment);
}

// Адаптер преобразует старый в новый
public class PaymentGatewayAdapter implements PaymentProcessor {
    private final OldPaymentGateway oldGateway;
    
    public PaymentGatewayAdapter(OldPaymentGateway oldGateway) {
        this.oldGateway = oldGateway;
    }
    
    @Override
    public PaymentResult process(Payment payment) {
        boolean success = oldGateway.charge(
            payment.getAmount(),
            payment.getCard().getNumber()
        );
        
        return success ? 
            PaymentResult.success() : 
            PaymentResult.failure("Payment declined");
    }
}

Какие паттерны использую постоянно?

  1. Dependency Injection — в каждом сервисе
  2. Repository — абстракция для доступа к данным
  3. Strategy — выбор алгоритма
  4. Observer — асинхронная коммуникация
  5. Builder — создание сложных объектов
  6. Factory — создание объектов

Антипаттерны, которых избегаю

  • Singleton с состоянием — сложность с тестированием и многопоточностью
  • God Object — компонент, который всё делает
  • Feature Envy — объект работает с данными другого объекта
  • Circular dependency — A зависит от B, B зависит от A

Практический совет

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

С какими паттернами программирования работал | PrepBro