Какой знаешь самый распространенный паттерн?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Какой знаешь самый распространенный паттерн
В Java существуют десятки паттернов проектирования, но несколько из них встречаются буквально в каждом проекте. Я расскажу о самых распространённых и их практическом применении.
1. Singleton (Одиночка)
Это, вероятно, самый используемый паттерн в Java. Гарантирует, что класс имеет только один экземпляр в приложении.
Где встречается:
- Database connections pool
- Logger instances
- Configuration readers
- Thread pools
// Базовая реализация (потокобезопасная)
public class DatabaseConnectionPool {
private static volatile DatabaseConnectionPool instance;
private DatabaseConnectionPool() {
// Приватный конструктор
}
public static DatabaseConnectionPool getInstance() {
if (instance == null) {
synchronized (DatabaseConnectionPool.class) {
if (instance == null) {
instance = new DatabaseConnectionPool();
}
}
}
return instance;
}
}
// Использование
DatabaseConnectionPool pool = DatabaseConnectionPool.getInstance();
Connection conn = pool.getConnection();
Лучший способ (Enum):
public enum DatabaseConnectionPool {
INSTANCE;
private HikariCP hikari;
DatabaseConnectionPool() {
this.hikari = new HikariCP();
}
public Connection getConnection() {
return hikari.getConnection();
}
}
// Использование
Connection conn = DatabaseConnectionPool.INSTANCE.getConnection();
2. Dependency Injection (DI)
В Spring это встроено, но это по-прежнему паттерн, и он критичен для качественного кода.
Проблема без DI:
public class UserService {
private DatabaseConnection db = new DatabaseConnection(); // tight coupling!
public User findUser(Long id) {
return db.query("SELECT * FROM users WHERE id = ?", id);
}
}
// Сложно тестировать!
С Dependency Injection:
public class UserService {
private final DatabaseConnection db;
// Конструктор инъекции
public UserService(DatabaseConnection db) {
this.db = db; // loose coupling!
}
public User findUser(Long id) {
return db.query("SELECT * FROM users WHERE id = ?", id);
}
}
// В Spring
@Service
public class UserService {
private final UserRepository userRepository;
// Spring автоматически инъектирует
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
}
Преимущества:
- Тестируется через mock'и
- Слабая связанность
- Гибкость и переиспользование
3. Factory (Фабрика)
Отделяет создание объектов от их использования. Очень частый паттерн.
Простой пример:
public class PaymentMethodFactory {
public static PaymentProcessor createProcessor(PaymentType type) {
switch (type) {
case CREDIT_CARD:
return new CreditCardProcessor();
case PAYPAL:
return new PayPalProcessor();
case CRYPTOCURRENCY:
return new CryptoProcessor();
default:
throw new IllegalArgumentException("Unknown payment type");
}
}
}
// Использование
PaymentProcessor processor = PaymentMethodFactory.createProcessor(PaymentType.PAYPAL);
processor.process(100.0);
В Spring (более красиво):
@Component
public class PaymentProcessorFactory {
private final Map<PaymentType, PaymentProcessor> processors;
public PaymentProcessorFactory(
CreditCardProcessor creditCardProcessor,
PayPalProcessor payPalProcessor,
CryptoProcessor cryptoProcessor
) {
this.processors = Map.of(
PaymentType.CREDIT_CARD, creditCardProcessor,
PaymentType.PAYPAL, payPalProcessor,
PaymentType.CRYPTOCURRENCY, cryptoProcessor
);
}
public PaymentProcessor getProcessor(PaymentType type) {
return processors.getOrDefault(type, () -> {
throw new IllegalArgumentException("Unknown payment type");
});
}
}
4. Builder (Строитель)
Строит сложные объекты пошагово. Очень удобен для объектов с множеством параметров.
public class HttpRequest {
private final String url;
private final String method;
private final Map<String, String> headers;
private final String body;
private final int timeout;
// Приватный конструктор
private HttpRequest(HttpRequest.Builder builder) {
this.url = builder.url;
this.method = builder.method;
this.headers = builder.headers;
this.body = builder.body;
this.timeout = builder.timeout;
}
// Builder класс
public static class Builder {
private String url;
private String method = "GET";
private Map<String, String> headers = new HashMap<>();
private String body = "";
private int timeout = 5000;
public Builder(String url) {
this.url = url;
}
public Builder method(String method) {
this.method = method;
return this;
}
public Builder header(String key, String value) {
this.headers.put(key, value);
return this;
}
public Builder body(String body) {
this.body = body;
return this;
}
public Builder timeout(int timeout) {
this.timeout = timeout;
return this;
}
public HttpRequest build() {
return new HttpRequest(this);
}
}
}
// Использование (очень читаемо!)
HttpRequest request = new HttpRequest.Builder("https://api.example.com/users")
.method("POST")
.header("Content-Type", "application/json")
.header("Authorization", "Bearer token")
.body("{\"name\": \"John\"}")
.timeout(10000)
.build();
5. Strategy (Стратегия)
Позволяет выбирать алгоритм во время выполнения. Часто используется с if-else.
// Без паттерна (плохо)
public double calculateDiscount(User user) {
if (user.isVIP()) {
return user.getTotalSpent() * 0.20; // 20% скидка
} else if (user.isPremium()) {
return user.getTotalSpent() * 0.10; // 10% скидка
} else {
return user.getTotalSpent() * 0.05; // 5% скидка
}
}
// С паттерном (хорошо)
interface DiscountStrategy {
double calculate(double amount);
}
class VIPDiscountStrategy implements DiscountStrategy {
public double calculate(double amount) {
return amount * 0.20;
}
}
class PremiumDiscountStrategy implements DiscountStrategy {
public double calculate(double amount) {
return amount * 0.10;
}
}
class RegularDiscountStrategy implements DiscountStrategy {
public double calculate(double amount) {
return amount * 0.05;
}
}
public class DiscountCalculator {
private DiscountStrategy strategy;
public void setStrategy(DiscountStrategy strategy) {
this.strategy = strategy;
}
public double applyDiscount(double amount) {
return strategy.calculate(amount);
}
}
// Использование
DiscountCalculator calculator = new DiscountCalculator();
if (user.isVIP()) {
calculator.setStrategy(new VIPDiscountStrategy());
} else if (user.isPremium()) {
calculator.setStrategy(new PremiumDiscountStrategy());
}
double discount = calculator.applyDiscount(user.getTotalSpent());
Рейтинг распространённости в реальных проектах
1. Singleton ⭐⭐⭐⭐⭐ (везде)
2. Dependency Injection ⭐⭐⭐⭐⭐ (Spring, везде)
3. Factory ⭐⭐⭐⭐⭐ (очень часто)
4. Builder ⭐⭐⭐⭐ (частые объекты)
5. Strategy ⭐⭐⭐⭐ (алгоритмы)
6. Observer ⭐⭐⭐ (события)
7. Adapter ⭐⭐⭐ (интеграции)
8. Decorator ⭐⭐ (расширения)
9. Template Method ⭐⭐ (базовые классы)
10. Proxy ⭐⭐ (кеширование, логирование)
Когда НЕ использовать паттерны
Важное правило: паттерны — это инструменты, не цель!
- ❌ Не добавляй паттерны "на будущее"
- ❌ Не усложняй код простых объектов
- ✅ Используй паттерны, когда видишь конкретную проблему
- ✅ Рефакторь в паттерн, когда код становится сложным
Вывод: Singleton, DI, Factory и Builder — это основные паттерны, которые ты встретишь в каждом Java проекте. Остальные используются реже, но важно их знать для решения специфических проблем.