Какой паттерн проектирования реализуют Generics?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
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 Method | Strategy |
|---|---|---|
| Гибкость | Через наследование | Через композицию |
| Типизация | Generics параметр класса | Generics параметр интерфейса |
| Переиспользование | Наследование классов | Композиция объектов |
| Сложность | Проще для простых случаев | Лучше для множественных вариантов |
| Runtime flexibility | Низкая | Высокая |
// Template Method - наследование
public class UserProcessor extends DataProcessor<User> {}
// Strategy - композиция
public class ProcessingService<T> {
private DataStrategy<T> strategy;
// Стратегия передаётся в constructor или method
}
Заключение
Generics реализуют паттерны проектирования:
- Template Method — самый прямой паттерн через generic параметры класса
- Strategy — через generic интерфейсы и функциональные интерфейсы
- Factory — через reflection и type information
- DAO/Repository — через generic базовые классы
- Decorator — через вложенные generics
Главная цель Generics — обеспечить type safety и code reuse без потери типизации, что значительно улучшает качество и надёжность кода.