- Que sont les modèles de conception ?
- Quels modèles de conception connaissez-vous ?
- Parlez-nous du modèle Singleton ? Comment le rendre thread-safe ?
- Parlez-nous du patron Factory ?
- Parlez-nous du modèle AbstractFactory
- Parlez-nous du modèle Adaper, de ses différences avec Wrapper ?
- Parlez-nous du modèle Proxy
- Qu'est-ce qu'un itérateur ? Quelles interfaces liées à l’itérateur connaissez-vous ?
- Pourquoi avons-nous besoin de la classe Arrays ?
- Pourquoi avons-nous besoin de la classe Collections ?
-
Les modèles de conception sont des solutions efficaces et bien établies aux problèmes les plus courants qui surviennent lors de la conception et du développement de programmes ou de leurs parties.
-
Singleton
,Factory
,Abstract Factory
,Template method
,Strategy
,Pool
,Adapter
,Proxy
,Bridge
,MVC
. -
Lorsque vous n'avez besoin que d'une seule instance d'une classe dans un programme, le modèle est utilisé
Singleton
. Cela ressemble à ceci (initialisation paresseuse) :clas Singleton { private Singleton instance; private Singleton() {} public static Singletot getInstance() { if (instance == null) instance = new Singleton(); return instance; } }
Pour le rendre thread-safe, vous pouvez ajouter
getInstance()
un modificateur à la méthodesynchronized
. Mais ce ne sera pas la meilleure solution (mais la plus simple). Une bien meilleure solution consiste à écrire la méthodegetInstance
de cette façon (verrouillage revérifié) :public static synchronized Singleton getInstance() { if (instance == null) synchronized(Singleton.class) { instance = new Singleton(); } return instance; }
-
Un modèle
Factory
est un modèle génératif. Il permet de créer des objets à la demande (par exemple sous certaines conditions). Cela ressemble à ceci :class Factory{ public static Object1 getObject1() { return new Object1(); } public static Object2 getObject2() { return new Object2(); } public static Object3 getObject3() { return new Object3(); } }
Il existe également une variante de ce modèle appelée
FactoryMethod
. Selon ce modèle, différents objets sont créés dans une seule méthode, en fonction des données d'entrée entrantes (valeurs des paramètres). Tous ces objets doivent avoir un ancêtre commun (ou une interface commune implémentable). Cela ressemble à ceci :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; } } }
Classes
Object1
etObject2
hériteObject3
de la classeCommonClass
. -
Un modèle
Abstract Factory
est également un modèle de conception génératif. Selon ce modèle, une usine abstraite est créée qui sert de modèle à plusieurs usines concrètes. Voici un exemple :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(); } }
-
Un modèle
Adapter
est un modèle structurel. Son implémentation permet d'utiliser un objet d'un type là où un objet d'un autre type est requis (généralement des types abstraits). Un exemple d'implémentation de ce modèle :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
— это структурный паттерн проектирования. Он нужен для того, чтобы контролировать доступ к Howому-то an objectу. Для этого пишется класс по типу "обёртка", то есть внутрь класса передаётся исходный an object, реализующий некий интерфейс, сам класс тоже реализует этот интерфейс, и в каждом методе этого класса вызывается похожий метод у исходного an object. Реализация того же интерфейса, что и у исходного an object, позволяет подменить исходный an object прокси-an objectом. Также это позволяет, не меняя исходного an object, "навешивать" на его методы Howую-то специальную дополнительную функциональность (например, логирование, проверка прав доступа, кэширование и т.д.). Пример: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); }
-
Итератор — это специальный внутренний an object коллекции, который позволяет последовательно перебирать элементы этой коллекций. Этот an object должен реализовывать интерфейс
Iterator<E>
, либоListIterator<E>
(для списков). Также, для того, чтобы перебирать элементы коллекции, коллекция должна поддерживать интерфейсIterable<E>
. ИнтерфейсIterable<E>
содержит всего один метод —iterator()
, который позволяет извне получить доступ к итератору коллекции.Интерфейс
Iterator<E>
содержит следующие методы:-
boolean hasNext()
— проверяет, есть ли в коллекции ещё Howой-то элемент -
E next()
— позволяет получить очередной элемент коллекции (после получения element, внутренний курсор итератора передвигается на следующий элемент коллекции) -
void remove()
— удаляет текущий элемент из коллекции
Интерфейс же
ListIterator<E>
содержит такие методы:-
boolean hasNext()
— проверяет, существуют ли ещё один элемент в коллекции (следующий за текущим) -
E next()
— возвращает очередной элемент коллекции (и передвигает внутренний курсок итератора на следующий элемент) -
int nextIndex()
— возвращает индекс следующего element -
void set(E e)
— устанавливает meaning текущего elementvoid add(E e)
. Добавляет элемент в конец списка. -
boolean hasPrevious()
— проверяет, существует ли Howой-то элемент в коллекции перед данным элементом -
E previous()
— возвращает текущий элемент коллекции и переводит курсор на предыдущий элемент коллекции -
int previousIndex
— возвращает индекс предыдущего element коллекции -
void remove()
— удаляет текущий элемент коллекции -
void add(E e)
— добавляет элемент e после текущего element коллекции
-
-
La classe
Arrays
est une classe utilitaire conçue pour diverses manipulations de tableaux. Cette classe a des méthodes pour transformer un tableau en liste, rechercher dans un tableau, copier un tableau, comparer des tableaux, obtenir un hashcode de tableau, représenter un tableau sous forme de chaîne, etc. -
La classe
Collections
est une classe utilitaire pour travailler avec des collections. Cette classe possède des méthodes pour ajouter des éléments à une collection, remplir une collection avec des éléments, rechercher une collection, copier une collection, comparer une collection, trouver les éléments maximum et minimum d'une collection, ainsi que des méthodes pour obtenir des modifications spécifiques des collections de types connus (par exemple, vous pouvez obtenir une collection thread-safe ou une collection immuable avec un élément).
GO TO FULL VERSION