Точного универсального ответа на этот вопрос нет
Есть стандартные решения для определенных задач – паттерны
Предложены хорошими специалистами
Проверены временем
Составляют удобный словарь для общения
Книга Э. Гаммы, Р. Хелма, Р. Джонсона и Дж. Влиссидеса – «банда четырех»
Вышла в 1995 году
Примеры на C++, кое-что на SmallTalk С тех пор ничего принципиально нового не придумали
Примеры для других языков
Несколько новых паттернов
Что такое паттерн проектирования
Типовая схема решения определенной задачи
Название (возможно, несколько)
Описание задачи
Схема решения
Обсуждение результатов
Названия:
«Observer» (GoF) / «Listener» (JDK)
Задача: один объект должен оповещать несколько других о том, что произошло некоторое событие (например, его состояние изменилось) Решение: создать интерфейс Listener
регистрировать слушателей в объекте-сервере
Готовый класс
Структуры данных Потоки и т. д.
Архитектура в целом
Client-Server
Описание взаимодействия объектов и классов, адаптированных для решения общей задачи проектирования в конкретном контексте
Чтобы создать хороший дизайн ☺
Инкапсуляция
Повторное использование
Снижение зависимостей
…
Порождающие паттерны
Организуют создание объектов
Структурные паттерны
Организуют структуру классов
Паттерны поведения
Организуют поведение объектов
Пример: Observer – паттерн поведения
Несколько основных паттернов
Singleton – порождение
String.CASE_INSENSITIVE_ORDER
Template Method – поведение
AbstractCollection
Adapter – структура
StringReader
Strategy (Delegate) – поведение
Comparator
Abstract Factory - порождение
Может существовать всего один объект данного класса
Например, когда важно только поведение, а не состояние (Comparator)
Отличие от класса со статической реализацией: полиморфизм Примеры:
String.CASE_INSENSITIVE_ORDER
Null Object
Boolean (TRUE и FALSE)
private-конструктор
Единственный объект - константа Ленивая инициализация:
не нужно создавать объект, если он никому не понадобился
public class Singleton { private static Singleton INSTANCE; private Singleton() {
} public static Singleton getInstance() { if (INSTANCE == null) {
INSTANCE = new Singleton();
} return INSTANCE;
}
}
Метод, инкапсулирующий создание объекта
Возможность кеширования
Integer.valueOf(int)
Возможность подмены реализации
В будущем можно возвращать объекты классанаследника, не изменяя клиентов при явном вызове конструктора так не получится
public class Singleton {
public static final Singleton INSTANCE = new Singleton();
private Singleton() {
}
}
Метод регламентирует порядок вызова других методов, реализуемых в подклассах Примеры:
AbstractCollection
Virtual Interface
Сам метод, как правило, объявляется final
Методы, которые он вызывает, обычно объявляются protected абстрактные, но могут иметь и «реализацию по умолчанию»
public abstract class TemplateMethodExample {
public final void perform() { try {
doPerform();
} catch (IllegalStateException e) { handleException(e);
} } protected abstract void doPerform() throws IllegalStateException;
protected abstract void handleException(IllegalStateException e);
}
Частный случай Template method
Полиморфный метод для создания объектов
В подклассах его можно переопределить, чтобы создавать объекты специфичных классов
public abstract class MultiMap {
private final Map map = new HashMap();
public Object get(Object[] keys) { return map.get(createKey(keys));
}
public void put(Object[] keys, Object value) { map.put(createKey(keys), value);
}
public abstract Object createKey(Object[] keys);
}
Задача: для некоторой цели нужно использовать класс, API которого для этой цели не подходит
Решение: создать класс с нужным интерфейсом, делегирующий существенные операции исходному классу Примеры:
StringReader
Класс-адаптер имеет конструктор, принимающий объект адаптируемого класса
Как правило, есть метод, возвращающий «завернутый» объект
public class IntAsBitList extends
AbstractList<Integer> {
private int value; public IntAsBitList(int x) { value = x;
} public Integer get(int index) { if ((index < 0) || (index > size())) { throw new IndexOutOfBoundsException();
} return (value & (1 << index)) >> index;
} public int size() { return 32;
}
}
Замена наследования делегированием
Иногда заменяет множественное наследование классов
Создание представлений subList
checkedCollection
Здесь правильнее употреблять название Wrapper - обертка
Задача: передать участок кода в качестве значения (например, параметра методу) Решение: определить API этого кода, реализовать его и передать объект реализации Примеры:
Comparator
Observer (Listener)
LayoutManager
Как правило, API регламентируется интерфейсом
Часто для реализации применяются анонимные inner-классы
Стратегии часто бывают синглетонами
public class StrategyExample { interface Filter { // Стратегия boolean accept(char c);
} public static String filterString(String s, Filter f) { StringBuilder b = new StringBuilder(); for (int i = 0; i < s.length(); i++) { if (f.accept(s.charAt(i))) {
b.append(s.charAt(i));
} } return b.toString();
}
}
public static void main(String[] args) {
Filter strategy = new Filter() { public boolean accept(char c) { return Character.isDigit(c);
}
};
System.out.println( filterString("123hgf231", strategy));
}
public static void main(String[] args) {
System.out.println( filterString("123hgf231", new Filter() { public boolean accept
Уважаемый посетитель!
Чтобы распечатать файл, скачайте его (в формате Word).
Ссылка на скачивание - внизу страницы.