← Назад к вопросам
С какими паттернами программирования работал
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");
}
}
Какие паттерны использую постоянно?
- Dependency Injection — в каждом сервисе
- Repository — абстракция для доступа к данным
- Strategy — выбор алгоритма
- Observer — асинхронная коммуникация
- Builder — создание сложных объектов
- Factory — создание объектов
Антипаттерны, которых избегаю
- Singleton с состоянием — сложность с тестированием и многопоточностью
- God Object — компонент, который всё делает
- Feature Envy — объект работает с данными другого объекта
- Circular dependency — A зависит от B, B зависит от A
Практический совет
Паттерны — это инструменты, не закон. Используй их когда они решают проблему, а не добавляют сложность.