
- Что такое паттерны проектирования?
- Какие паттерны проектирования вы знаете?
- Расскажите про паттерн Singleton? Как сделать его потокобезопасным?
- Расскажите про паттерн Factory?
- Расскажите про паттерн AbstractFactory
- Расскажите про паттерн Adaper, его отличия от Wrapper?
- Расскажите про паттерн Proxy
- Что такое итератор? Какие интерфейсы, связанные с итератором, вы знаете?
- Зачем нужен класс Arrays?
- Зачем нужен класс Collections?
Паттерны проектирования — это устоявшиеся удачные решения самых распространнёх проблем, возникающих при проектировании и разработке программ или их частей.
Singleton
,Factory
,Abstract Factory
,Template method
,Strategy
,Pool
,Adapter
,Proxy
,Bridge
,MVC
.Когда нужно, чтобы в программе существовал только один экземпляр какого-то класса, то применяют паттерн
Singleton
. Он выглядит так (lazy initialization):clas Singleton { private Singleton instance; private Singleton() {} public static Singletot getInstance() { if (instance == null) instance = new Singleton(); return instance; } }
Чтобы сделать его потокобезопасным, можно добавить к методу
getInstance()
модификаторsynchronized
. Но это будет не самым лучшим решением (зато самым простым). Гораздно лучшее решение — это написать методgetInstance
таким образом (double-checked locking):public static synchronized Singleton getInstance() { if (instance == null) synchronized(Singleton.class) { instance = new Singleton(); } return instance; }
Паттерн
Factory
— это порождающий паттерн. Он позволяет создавать объекты по требованию (например, при определённых условиях). Выглядит это так:class Factory{ public static Object1 getObject1() { return new Object1(); } public static Object2 getObject2() { return new Object2(); } public static Object3 getObject3() { return new Object3(); } }
Также существует разновидность этого паттерна под названием
FactoryMethod
. Согласно этому паттерну, в одном методе создаются разные объекты, в завимости от поступающих входных данных (значений параметров). Все эти объекты должны иметь общего предка (или один общий реализуемый интерфейс). Выглядит он так:class FactoryMethod { public enum TypeObject { TYPE1, TYPE2, TYPE3 } public static CommonClass getObject(TypeObject type) { switch(type) { case TYPE1: return new Object1(); case TYPE2: return new Object2(); case TYPE3: return new Object3(); default: return null; } } }
Классы
Object1
,Object2
иObject3
наследуются от классаCommonClass
.Паттерн
Abstract Factory
— это также порождающий шаблон проектирования. Согласно этому паттерну, создаётся некоторая абстрактная фабрика, служащая шаблоном для нескольких конкретных фабрик. Можно привести такой пример:class Human {} class Boy extends Human {} class TeenBoy extends Human {} class Man extends Human {} class OldMan extends Human {} class Girl extends Human {} class TeenGirl extends Human {} class Woman extends Human {} class OldWoman extends Human {} interface AbstractFactory { Human getPerson(int age); } class FactoryMale implements AbstractFactory { public Human getPerson(int age) { if (age < 12) return new Boy(); if (age >= 12 && age <= 20) return new TeenBoy(); if (age > 20 && age < 60) return new Man(); return new OldMan(); } } сlass FactoryFemale implements AbstractFactory { public Human getPerson(int age) { if (age < 12) return new Girl(); if (age >= 12 && age <= 20) return new TeenGirl(); if (age > 20 && age < 60) return new Woman(); return new OldWoman(); } }
Паттерн
Adapter
— это структурный паттерн. Его реализация позволяет использовать объект одного типа там, где требуется объект другого типа (обычно это абстрактные типы). Пример реализации этого паттерна:interface TotalTime { int getTotalSeconds(); } interface Time { int getHours(); int getMinutes(); int getSeconds(); } class TimeAdapter extends TotalTime { private Time time; public TimeAdapter(Time time) { this.time = time; } public int getTotalTime() { return time.getSeconds + time.getMinutes * 60 + time.getHours * 60 * 60; } } class TotalTimeAdapter extends Time { private TotalTime totalTime; public TotalTimeAdapter(TotalTime totalTime) { this.totalTime = totalTime; } public int getSeconds() { return totalTime % 60; } public int getMinutes() { return (totalTime / 60) % 60; } public int getHours() { return totaltime/ (60 * 60) ; } } class Main { public static void main(String[] args) { Time time = new Time() { public int getSeconds() { return LocalTime.now().getSecond(); } public int getMinutes() { return LocalTime.now().getMinute(); } public int getHours() { return LocalTime.now().getHour() ; } }; TotalTime totalTime = new TimeAdapter(time); System.out.println(totalTime.getTotalSeconds()); TotalTime totalTime2 = new TotalTime() { public int getTotalSeconds() { LocalTime currTime = LocalTime.now(); return currTime.getSecond() + currTime.getMinute * 60 + currTime.getHour * 60 * 60; } }; Time time2 = new TotalTimeAdapter(totalTime2); System.out.println(time2.getHours + ":" + time2.getMinutes() + ":" + time2.getSeconds()); } }
При реализации паттерна
Wrapper
создаётся класс, который оборачивает исходный класс и реализует тот же интерфейс, который реализует исходный класс. Таким образом, это позволяет расширить функциональность исходного класса и использовать новый класс там, где ожидается использование исходного класса. Это отличается от реализации паттернаAdapter
тем, что в данном случае используется один интерфейс (тот же, что есть у исходного класса). В паттернеAdapter
же используется два интерфейса, и класс, который оборачивает экземпяр исходного класса, реализует совсем другой инферфейс, не интерфейс исходного класса.Паттерн
Proxy
— это структурный паттерн проектирования. Он нужен для того, чтобы контролировать доступ к какому-то объекту. Для этого пишется класс по типу "обёртка", то есть внутрь класса передаётся исходный объект, реализующий некий интерфейс, сам класс тоже реализует этот интерфейс, и в каждом методе этого класса вызывается похожий метод у исходного объекта. Реализация того же интерфейса, что и у исходного объекта, позволяет подменить исходный объект прокси-объектом. Также это позволяет, не меняя исходного объекта, "навешивать" на его методы какую-то специальную дополнительную функциональность (например, логирование, проверка прав доступа, кэширование и т.д.). Пример:interface Bank { void setUserMoney(User user, double money); double getUserMoney(User user); } class CitiBank implements Bank { //оригинальный класс public void setUserMoney(User user, double money) { UserDAO.update(user,money); } public double getUserMoney(User user) { UserDAO.getUserMoney(user); } } class SecurityProxyBank implements Bank { private Bank bank; public SecurityProxyBank(Bank bank) { this.bank = bank; } public void setUserMoney(User user, double money) { if (!SecurityManager.authorize(user,BankAccounts.Manager) throw new SecurityException("User can't change money value"); UserDAO.update(user,money); } public double getUserMoney(User user) { if (!SecurityManager.authorize(user,BankAccounts.Manager) throw new SecurityException("User can't get money value"); UserDAO.getUserMoney(user); }
Итератор — это специальный внутренний объект коллекции, который позволяет последовательно перебирать элементы этой коллекций. Этот объект должен реализовывать интерфейс
Iterator<E>
, либоListIterator<E>
(для списков). Также, для того, чтобы перебирать элементы коллекции, коллекция должна поддерживать интерфейсIterable<E>
. ИнтерфейсIterable<E>
содержит всего один метод —iterator()
, который позволяет извне получить доступ к итератору коллекции.Интерфейс
Iterator<E>
содержит следующие методы:boolean hasNext()
— проверяет, есть ли в коллекции ещё какой-то элементE next()
— позволяет получить очередной элемент коллекции (после получения элемента, внутренний курсор итератора передвигается на следующий элемент коллекции)void remove()
— удаляет текущий элемент из коллекции
Интерфейс же
ListIterator<E>
содержит такие методы:boolean hasNext()
— проверяет, существуют ли ещё один элемент в коллекции (следующий за текущим)E next()
— возвращает очередной элемент коллекции (и передвигает внутренний курсок итератора на следующий элемент)int nextIndex()
— возвращает индекс следующего элементаvoid set(E e)
— устанавливает значение текущего элементаvoid add(E e)
. Добавляет элемент в конец списка.boolean hasPrevious()
— проверяет, существует ли какой-то элемент в коллекции перед данным элементомE previous()
— возвращает текущий элемент коллекции и переводит курсор на предыдущий элемент коллекцииint previousIndex
— возвращает индекс предыдущего элемента коллекцииvoid remove()
— удаляет текущий элемент коллекцииvoid add(E e)
— добавляет элемент e после текущего элемента коллекции
Класс
Arrays
— это утилитарный класс, предназначенный для разнообразных манипуляций с массивами. В этом классе есть методы превращения массива в список, поиска по массиву, копирования массива, сравнения массивов, получения хешкода массива, представление массива в виде строки и др.Класс
Collections
— это утилитарный класс для работы с коллекциями. В этом классе есть методы добавления элементов в коллекцию, наполнения коллекции элементами, поиска по коллекции, копировании коллекции, сравнения коллекции, нахождения максимального и минимального элементов коллекции, а также методы получения специфический модификаций коллекций известных типов (например, можно получить потокобезопасную коллекции или неизменяемую коллекцию с одним элементом).
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
В качестве дополнения:
Вопрос 4. Как такового Factory нет (см Design Patterns, GoF) — это либо AbstractFactory, либо Factory Method (которым, как отмечено ниже, обычно порождает объекты AbstractFactory).
Вопрос 6. По сути все верно. Несколько смущает, что Wrapper иногда называют синонимом Adapter, а иногда Decorator. Согласно Design Patterns (GoF) Wrapper — это Decorator.