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

Какой паттерн проектирования реализуют Generics?

2.0 Middle🔥 141 комментариев
#SOLID и паттерны проектирования

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

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

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

Generics и паттерн Template Method

Generics в Java реализуют несколько паттернов проектирования, но главным образом это Шаблонный метод (Template Method) и элементы Strategy паттерна. Обсудим оба подхода и их взаимосвязь.

Основной паттерн: Template Method

Template Method паттерн — это поведенческий паттерн, который определяет скелет алгоритма в методе базового класса, оставляя реализацию деталей подклассам.

Generics позволяют реализовать Template Method без наследования, через типизированные параметры:

// Обобщённый алгоритм обработки данных
public abstract class DataProcessor<T> {
    // Template Method
    public final void process(T data) {
        T validated = validate(data);          // Шаг 1
        T transformed = transform(validated);  // Шаг 2
        persist(transformed);                   // Шаг 3
    }
    
    // Методы для переопределения
    protected abstract T validate(T data);
    protected abstract T transform(T data);
    protected abstract void persist(T data);
}

// Конкретная реализация для User
public class UserProcessor extends DataProcessor<User> {
    @Override
    protected User validate(User user) {
        if (user.getEmail() == null) {
            throw new IllegalArgumentException("Email required");
        }
        return user;
    }
    
    @Override
    protected User transform(User user) {
        user.setName(user.getName().toUpperCase());
        return user;
    }
    
    @Override
    protected void persist(User user) {
        userRepository.save(user);
    }
}

// Использование
DataProcessor<User> userProcessor = new UserProcessor();
User user = new User("john@example.com", "john");
userProcessor.process(user);

Паттерн Strategy через Generics

Strategy паттерн — это альтернативный способ, где алгоритмы инкапсулируются в отдельные объекты.

Generics отлично подходят для типизирования Strategy:

// Strategy интерфейс
@FunctionalInterface
public interface Validator<T> {
    void validate(T data) throws ValidationException;
}

// Конкретные стратегии
public class EmailValidator implements Validator<String> {
    @Override
    public void validate(String email) throws ValidationException {
        if (!email.contains("@")) {
            throw new ValidationException("Invalid email");
        }
    }
}

public class AgeValidator implements Validator<Integer> {
    @Override
    public void validate(Integer age) throws ValidationException {
        if (age < 0 || age > 150) {
            throw new ValidationException("Invalid age");
        }
    }
}

// Generic контейнер для стратегий
public class DataValidator<T> {
    private List<Validator<T>> validators = new ArrayList<>();
    
    public void addValidator(Validator<T> validator) {
        validators.add(validator);
    }
    
    public void validate(T data) throws ValidationException {
        for (Validator<T> validator : validators) {
            validator.validate(data);
        }
    }
}

// Использование
DataValidator<String> emailValidator = new DataValidator<>();
emailValidator.addValidator(new EmailValidator());
emailValidator.validate("john@example.com"); // OK
emailValidator.validate("invalid"); // Exception

Типобезопасность через Generics

Generics обеспечивают compile-time type safety — проверка типов на этапе компиляции:

// ❌ Без Generics - небезопасно
List list = new ArrayList();
list.add("string");
list.add(123);
list.add(new User());

for (Object obj : list) {
    String s = (String) obj; // Может упасть в runtime!
}

// ✅ С Generics - безопасно
List<String> typedList = new ArrayList<>();
typedList.add("string");    // OK
typedList.add(123);         // Compilation ERROR
typedList.add(new User()); // Compilation ERROR

for (String s : typedList) {
    // s уже имеет тип String, cast не нужен
}

Generics и Type Parameters

Простой Generic класс

public class Container<T> {
    private T value;
    
    public void setValue(T value) {
        this.value = value;
    }
    
    public T getValue() {
        return value;
    }
}

// Использование
Container<String> stringContainer = new Container<>();
stringContainer.setValue("hello");
String value = stringContainer.getValue(); // Тип известен

Container<Integer> intContainer = new Container<>();
intContainer.setValue(42);
Integer intValue = intContainer.getValue();

Bounded Type Parameters

// Ограничиваем типы параметра
public class NumberProcessor<T extends Number> {
    public double toDouble(T value) {
        return value.doubleValue();
    }
}

// Использование
NumberProcessor<Integer> intProc = new NumberProcessor<>();
NumberProcessor<Double> doubleProc = new NumberProcessor<>();
// NumberProcessor<String> invalid; // Compilation ERROR

Wildcard типы

// ? extends - верхняя граница
public void printList(List<? extends Number> list) {
    for (Number num : list) {
        System.out.println(num);
    }
}

// ? super - нижняя граница
public void addNumbers(List<? super Integer> list) {
    list.add(10);
    list.add(20);
}

// Использование
List<Integer> integers = Arrays.asList(1, 2, 3);
List<Double> doubles = Arrays.asList(1.0, 2.0, 3.0);

printList(integers); // OK - Integer extends Number
printList(doubles);  // OK - Double extends Number

Generics в Collections Framework

Java Collections Framework построена на Generics и использует Template Method паттерн:

// AbstractList<E> - Template Method паттерн
public abstract class AbstractList<E> implements List<E> {
    public boolean add(E e) {
        add(size(), e);
        return true;
    }
    
    // Это template method - определяет скелет
    public int indexOf(Object o) {
        for (int i = 0; i < size(); i++) {
            if (Objects.equals(o, get(i))) {
                return i;
            }
        }
        return -1;
    }
}

// Конкретная реализация
public class ArrayList<E> extends AbstractList<E> {
    private Object[] elementData;
    
    @Override
    public E get(int index) {
        return (E) elementData[index];
    }
}

Паттерны, реализуемые через Generics

1. Factory паттерн

public class GenericFactory<T> {
    private Class<T> type;
    
    public GenericFactory(Class<T> type) {
        this.type = type;
    }
    
    public T create() throws Exception {
        return type.getDeclaredConstructor().newInstance();
    }
}

GenericFactory<User> userFactory = new GenericFactory<>(User.class);
User user = userFactory.create();

2. DAO паттерн

public abstract class GenericDAO<T, ID> {
    public abstract T findById(ID id);
    public abstract void save(T entity);
    public abstract void update(T entity);
    public abstract void delete(ID id);
}

public class UserDAO extends GenericDAO<User, Long> {
    @Override
    public User findById(Long id) {
        // Реализация для User
    }
}

3. Repository паттерн

public interface Repository<T, ID> {
    T findById(ID id);
    List<T> findAll();
    void save(T entity);
    void delete(T entity);
}

public interface UserRepository extends Repository<User, Long> {
    User findByEmail(String email);
}

Сравнение Template Method и Strategy через Generics

АспектTemplate MethodStrategy
ГибкостьЧерез наследованиеЧерез композицию
ТипизацияGenerics параметр классаGenerics параметр интерфейса
ПереиспользованиеНаследование классовКомпозиция объектов
СложностьПроще для простых случаевЛучше для множественных вариантов
Runtime flexibilityНизкаяВысокая
// Template Method - наследование
public class UserProcessor extends DataProcessor<User> {}

// Strategy - композиция
public class ProcessingService<T> {
    private DataStrategy<T> strategy;
    // Стратегия передаётся в constructor или method
}

Заключение

Generics реализуют паттерны проектирования:

  1. Template Method — самый прямой паттерн через generic параметры класса
  2. Strategy — через generic интерфейсы и функциональные интерфейсы
  3. Factory — через reflection и type information
  4. DAO/Repository — через generic базовые классы
  5. Decorator — через вложенные generics

Главная цель Generics — обеспечить type safety и code reuse без потери типизации, что значительно улучшает качество и надёжность кода.

Какой паттерн проектирования реализуют Generics? | PrepBro