![]() |
Модераторы: LSD, AntonSaburov |
![]() ![]() ![]() |
|
Domestic Cat |
|
||||||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 5452 Регистрация: 3.5.2004 Где: Dallas, US Репутация: 50 Всего: 172 |
Simple Factory
Служит для создания объектов различных классов на основании информации, переданной во время выполнения программы. Обычно эти классы наследуют от базового класса или интерфейса, т.е. обладают похожей функциональностью. Пример В данном случае Фабрика производит объект Filter1 или Filter2 в зависимости от значения переданного в метод getFilter. Java
C#
Паттерн очень простой, потому и не включен в классические 23 паттерна. Преимущества Позволяет избавиться от хардкодинга. Например, без этого паттерна нам пришлось бы писать
Теперь представим что мы решили дать классам Filter<> более осмысленные имена. Нам пришлось бы прочесывать весь код и менять не один раз. С данным паттерном все просто, достаточно изменить класс SimpleFactory. Паттерн добавляет слой абстракции, отделяя процесс создания объекта от его использования. Фабрика может применять более сложную логику создания объектов; например может создавать их через рефлекшн. -------------------- |
||||||
|
|||||||
Domestic Cat |
|
||||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 5452 Регистрация: 3.5.2004 Где: Dallas, US Репутация: 50 Всего: 172 |
Factory
Используется, когда неизвестно, объект какого класса нужно будет создать. В отличие от SimpleFactory в Factory отсутствует явный код выбора класса (как в методе getFilter() в предыдущем случае). Этот выбор может осуществляться через полиморфизм. Пример Есть несколько классов Filter<>, наследующих от абстрактного Filter. Каждый из фильтров использует свои ImageFilter (и, вероятно еще к-л компоненты). Java
C#
Суть паттерна в том, что обязанность выбора класса ImageFilter ложится на плечи сабклассов. Тем не менее точка доступа к ImageFilter одна - метод getImageFilter() базового класса. Преимущества. Фактически, те же, что и у SimpleFactory. Логика получения объектов (ImageFilter) отделена от основного кода. -------------------- |
||||
|
|||||
Domestic Cat |
|
||||||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 5452 Регистрация: 3.5.2004 Где: Dallas, US Репутация: 50 Всего: 172 |
Abstract Factory
Несмотря на название, использовать абстрактный класс в этом паттерне необязательно. Самое главное отличие его от Factory в том, что один класс предоставляет интерфейс для доступа к группе объектов, а не одному объекту. Пример Мы пишем игру. Есть класс Level, от которого наследуют Level1, Level2 и т п. а каждом уровне - свои монстры двух типов - "слабый" и "сильный". Будем считать что все классы <..>Monster наследуют от некого класса Monster. Java
C#
Здесь класс Level и является Абстрактной Фабрикой, т.к. дает доступ к группе объектов (weakMonster, strongMonster). Теперь можно писать такой код:
Таким образом, основной код понятия не имеет, каких монстров он использует. Преимущества. Использование Abstract Factory, как и Factory, позволяет писать код, который "не знает" какие конкретно объекты он использует. Наследование позволяет сабклассам самим создавать об3екты, которые им нужны; полиморфизм дает возможность использовать их. Это упрощает как написание, так и поддержку кода, делает его более робастным. -------------------- |
||||||
|
|||||||
Domestic Cat |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 5452 Регистрация: 3.5.2004 Где: Dallas, US Репутация: 50 Всего: 172 |
Singleton
"Одиночка". Паттерн проименяется в случае, когда важно, чтобы в данный момент времени существовал один единственный объект какого-либо класса. Зачем это нужно? Предположим мы написали простую базу данных. Наше АПИ включает в себя класс LockManager. Объект этого класса занимается тем, что предотвращает одновременный доступ клиентов к одному и тому же ряду в БД, то есть пока один клиент меняет запись (вызывает метод, передающий изменения), менеджер не позволяет никому другому эту запись изменить. Некий другой программер решил воспользоваться этим АПИ, и создал 2 объекта LockManager. Теперь возможна ситуация, когда 2 клиента попытаются одновременно менять одну запись. Что может привести к нехорошим последствиям. Естественно, нужно сделать так, чтобы можно было создать только 1 объект класса LockManager, а при попытке создать большее количество объектов, дать знать программеру об ошибке. В целом подход один: делаем приватный конструктор, храним инстанс синглтона в самом классе как статик поле; получаем его статик методом типа getInstance(). Есть несколько способов реализации Синглтона. 1. В getInstance() проверяем, если ссылка не нул, возвращаем нул. 2. Если ссылка не нул, кидаем иксепшн. Пример реализации 1. Java
Код на шарпе не привожу, т.к. он на 99% такой же. Вариант с иксепшном аналогичен; в строчке else return null; вместо ретурна нужно бросать иксепшн. Оба метода имеют свои преимущества и недостатки, но они настолько тонкие, что нет смысла о них здесь говорить. Преимущества. Дает полный контроль над количеством созданных объектов класса. Паттерн легко применить дкля случая когда нужно чтобы можно было создать не более N объектов; или точно N объектов. -------------------- |
|||
|
||||
Domestic Cat |
|
||||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 5452 Регистрация: 3.5.2004 Где: Dallas, US Репутация: 50 Всего: 172 |
Builder
Как следует из названия, паттерн собирает ряд объектов в одно целое, "строит" нечто новое. Сравним паттерн с Factory: Factory: вы делаете заказ на определенную машину, фабрика ее производит. Builder: вы даете строителю нужные материалы. Тот что-то из них делает. Этот паттерн часто используется для построения ГУИ. Например, мы читаем данные из БД, причем для удобства сортировки выносим один столбец со всеми возможными должностями работников отдельно, в виде JList или JComboBox (ListBox, ComboBox). Лист удобен тогда, когда элементов в списке мало, комбобокс - когда их много. Можно легко решить проблему следующим псевдокодом:
Другим решением было бы создать два класса - строителя, каждый был бы ответственен за построение определенного ГУИ. В приведенном выше случае создание двух классов було бы слишком; но если бы мы решили создать 2 ОЧЕНЬ разных ГУИ, то 2 Билдера было бы решением более "чистым". Совсем необязательно чтобы Строитель строил визуальные компоненты. Пример Это очень надуманный пример, поясняющий тем не менее суть паттерна. В роли Builderов выступают ArrayListCollection и ArrayCollection. Каждый создает свой тип коллекции. Для выбора Билдера используется паттерн SimpleFactory (метод GetMyCollection) C#
Преимущества. Билдер отделяет логику создания сложных компонентов от кода, использующего эти компоненты. Предположим мы на основе паттерна SimpleFactory выбираем Builder, который строит ГУИ для некого визуального компонента. Такой код легче поддерживать (ГУИ для каждого случая создается в своем классе/методе); легче реюзать; легче дополнять (например, третьим вариантом ГУИ) -------------------- |
||||
|
|||||
Domestic Cat |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 5452 Регистрация: 3.5.2004 Где: Dallas, US Репутация: 50 Всего: 172 |
Prototype
Прототип аналогичен паттерну Builder за одним исключением - он создает объекты, клонируя их из некоторого исходного объекта. Еще раз: Билдер создает объект из множества других; Прототип клонирует объект и преобразовывает его нужным образом. Пример Давайте попробуем обойтись без кода. Предположим, мы пишем АПИ, работающий с БД на низком уровне. Мы создали класс, который может держать таблицу реляционной БД и назвали его, скажем, DataHolder. Клиент может выполнять запрос и получать в свое распоряжение этот самый объект. Получает он его естественно, не для просто так, а чтоб поменять, например добавить ряд. Вопрос: как передать изменения в ДБ? Первый ответ - передать весь DataHolder. Нехорошесть этого подхода следует хотя бы из того, что чаще всего ДБ находится на другом компьютере, т.е. доступ осуществляетя удаленно. Передавать каждый раз объект, содержащий, возможно, десятки тысяч записей, по сети - не самая хорошая идея, особенно если изменен был всего один ряд. Выход такой: мы клонируем исходный DataHolder, остатвив в нем только измененые ряды; и передадим его на сервер. Это и есть паттерн Prototype. Слово "клонируем" не обязательно значит, что нужно пользоваться методом clone() (Clone()). В нашем случае клонировать DataHolder целиком было бы крайтне неэффективно. Скорее, это означает создание своего метода, который бы не только создавал DataHolder но и фильтровал данные оригинального DataHolder, прежде чем скопировать их. Реализация такого класса (и паттерна) в .NET называется DataSet; описанный паттерн использован в методе GetChanges(). (afair) Преимущества Те же, что и у Билдера. Логика создания нового объекта инкапсулирована в реализации Прототипа (т.е. в конкретных методах). Такие методы могут фильтровать, менять исходные данные. На сем креативные паттерны закончены. -------------------- |
|||
|
||||
Domestic Cat |
|
||||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 5452 Регистрация: 3.5.2004 Где: Dallas, US Репутация: 50 Всего: 172 |
Adapter
Adapter преобразует ("адаптирует") один интерфейс в другой интерфейс. Может существовать множество причин для такого преобразования, например: 1. Класс А имеет сложный интерфейс, скорее "низкоуровневый". Для многих операций приходится делать несколько вызовов методов этого класса. Имеет смысл переработать этот интерфейс в более простой. 2. Интерфейс класса А очень широк; но для определенных операций нам нужен более узкий, или специализированный интерфейс. Например, один и тот же класс может использоваься клиентами (удаленно), но может также быть использован администратором сервера. В таком объект не должен "показывать" клиентам всю функциональность. Есть 2 основных варианта реализации Адаптера: через наследование и через композицию. Примеры Java Через наследование. Мы написали класс BinaryWriter, который работает с бинарными файлами, при этом шифруя информацию нашим собственным алгоритмом. Но нам часто приходится писать текстовые данные. Применение Адаптера упрощает работу.
Теперь мы можем вместо вызовов нескольких методов класса BinaryWriter использовать append. Но класс BinaryStringWriter по-прежнему обладает всеми паблик методами класса BinaryWriter! Это пример так называемого Two-Way Adapter - двустороннего адаптера, который может выступать как BinaryStringWriter, так и BinaryWriter. Через композицию. Класс Account предоставляет интерфейс для управления аккаунтом клиента - снятием денег, удалением аккаунта и пр. Но показывать клиенту определенную функциональност, например метод deleteAccount, не слишком удачный вариант. Поэтому мы конструируем адаптер - класс ClientAccount, который содержит Account как приватное поле. Класс ClientAccount предоставляет клиенту лишь часть интерфейса этого поля.
Преимущества Адаптер - это что-то вроде "переходника" - он позволяет рассматривать один интерфейс по-разному. Например, можно изменить интерфейс класса JTree, сделав его похожим на интерфейс класса ArrayList или HashMap. Можно переделать интерфейс нескольких совершенно различных классов так, что он будет одним и тем же; такие адаптеры называются Plaggable Adapters. Если подумать, это очень интересная концепция. -------------------- |
||||
|
|||||
Domestic Cat |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 5452 Регистрация: 3.5.2004 Где: Dallas, US Репутация: 50 Всего: 172 |
Bridge
Адаптер преобразует интерфейс объекта, оставляя объект "нетронутым". Например, часы в наше время можно засунуть куда угодно, скажем в авторучку или чайник. Но часы останутся по сути своей часами; поменяется их вид и количество кнопочек, и только. Bridge (мост) делает прямо противоположное - он "фиксирует" интерфейс объекта. Хороший пример Моста - электрическая розетка. Все что нас волнует - это как в нее засунуть вилку и какое там напряжение; сама же электроэнергия может идти с ТЭЦ, ГЭС, АЭС, солнечноей батареи или ветряка - нам все равно. И если завтра вместо ветряка будут пользовать солнечную батарею или белку в колесе, мы этого не заметим. Часто Мост реализован как связка интерфейс-конкретный класс. Пример C# Объект класса Calculator производит некие вычисления в методе Calculate. По завершении метода он вызывает соответствыющие методы всех зарегистрированных слушателей. Слушатели в свою очередь наследуют от интерфейса IBridge и таким образом гарантированно обладают методом DoCalculationFinished. Перед вычислением клиентское приложение регистрирует класс слушателя и его метод DoCalculationFinished будет вызван при появлении события CalculationFinished. Таким образом, работа клиента основана на наличии класса с интерфейсом IBridge, больше его ничто не интересует. Как показывает пример, сам Мост может делать далее с данными что угодно - слать по сети или сохранять на диск.
Преимущества Паттерн сохраняет интерфейс клиента, сохраняя тем самым труд и время программера. Легко добавить нужную функциональность - например, можно добавить класс PrintBridge, который будет печатать результат, TransmitBridge, который отфильтрует данные и передаст их для дальнейших вычислений, и т п. -------------------- |
|||
|
||||
Domestic Cat |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 5452 Регистрация: 3.5.2004 Где: Dallas, US Репутация: 50 Всего: 172 |
Composite
Обычно мы работаем либо с индивидуальными объектами, либо с коллекциями. Паттерн Composite объединяет оба случая. Пример композита - дерево. Дерево имеет листья (листья не могут иметь детей) и ноды, который могут содержать другие ноды или листья. Суть паттерна в том, чтобы и ноды, и листья имели один интерфейс. Пример Java Предположим, что приложение описывает некую иерархическую структуру, например, сотрудников фирмы. Любой сотрудник будет описываться классом, наследующим от AbstractEmployee, т.е. интерфейс у всех классов будет один. AbstractEmployee по сути есть часть древовидной структуры, и хранит ссылку на родителя и детей. Сабклассы AbstractEmployee по-своему имплементруют соответствующие методы; так например класс Employee (обычный сотрудник) на дереве будет листом, т.е. getSubordinates должен возвращать null.
Преимущества Этот паттерн широко используется в Java и C#. Например, любая JPanel (Panel) содержит ссылки на свои компоненты (которые в свою очередь сами держат ссылки на их компоненты). Поскольку компоненты наследуют от Component (Control) то и интерфейс у них один. Композит позволяет упростить работу с похожими объектами. -------------------- |
|||
|
||||
Domestic Cat |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 5452 Регистрация: 3.5.2004 Где: Dallas, US Репутация: 50 Всего: 172 |
Decorator
Декоратор похож на Адаптер в том смысле, что он преобразует интерфейс исходного класса. Однако, как и в случае с многими паттернами, цель Декоратора другая. Его задача - изменить интерфейс нескольких классов. Адаптер удобно использовать, делая его сабклассом нужного класса. А если нужно 20 классов "привести" к одному интерфейсу? Нужно создавать 20 сабклассов. Другой момент - Декоратор часто используется в ГУИ. Суть паттерна в том, что создается класс, содержащий поле; класс этого поля является суперклассом (или интерфейсом) для тех классов, которые нам нужно "продекорировать". Пример Java Данный пример декорирует JPanel - когда указатель мыши находится над панелью, она обводится красной рамкой. В Декоратор мы помещаем не JPanel а JComponent; поэтому его можно использовать для декорации не только панели.
Преимущества Один Декоратор может менять интерфейс для любого количества компонентов. -------------------- |
|||
|
||||
Domestic Cat |
|
||||||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 5452 Регистрация: 3.5.2004 Где: Dallas, US Репутация: 50 Всего: 172 |
Facade
Что (отчасти) неудобно в ООП и при использовании паттернов - большое количество классов. Чем больше классов - тем сложнее работать. Фасад - это класс, который предоставляет простой интерфейс для работы, и прячет таким образом сложную бизнес логику приложения. Естественно, упрощение интерфейса приводит к потере гибкости при работе с Фасадом. Тем не менее, программист по-прежнему имеет доступ к "низкоуровневому" АПИ. Фасад - всего лишь класс, который упрощает жизнь в некоторых (или многих) случаях. Можно использовать несколько Фасадов, наследующх от одного класса. Пример Чтоб не писать чего-то сложного и нудного, все будет предельно схематично. Есть классы A, B, C. Каждый раз нам приходится визывать ряд методов этих классов:
Создадим класс Facade, в который вынесем Блок 1 и Блок 2 :
Теперь все упрощается
Преимущества Упрощает интерфейс. Клиенту теперь не нужно знать тонкостей работы с A, B и C. ВСе взаимодействие с кэтими классами ограничено двумя методами. С другой стороны, если придется поменять логику взаимодействия этих классов, вероятно, придется менять Фасад, или снова пользоваться исходными методами. -------------------- |
||||||
|
|||||||
Domestic Cat |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 5452 Регистрация: 3.5.2004 Где: Dallas, US Репутация: 50 Всего: 172 |
Flyweight
Объектно-ориентированный подход подразумевает создание классов, и естественно, объектов этих классов. Иногда нужно создать множество объектов одного класса. Если объекты отличаются только несколькими параметрами, применяется паттерн Flyweight. Рассмотрим классический пример - папки на диске. У каждой папки свое название и иконка. Естественно, создавать свой объект для каждой папки - значит зодать множество почти одинаковых объектов. Паттерн Flyweight означает следующее: нужно написать один класс, создать один объект этого класса, и в аргументы соответствующих методов передавать нужные параметры (название и имадж папки), а не создавать свой объект для каждой. Что-то вроде такого:
Flyweight используется нечасто, потому весь пример я опущу (он очевиден, к тому же). Но зато существует несколько разновидностей паттерна, одна из которых Sharable Пусть у нас есть класс, объекты которого получаются очень большими. Тогда можно пользовать паттерн Sharable, который по сути есть Flyweight , но намерение другое - предоставить один и тот же объект для различных клиентов. -------------------- |
|||
|
||||
Domestic Cat |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 5452 Регистрация: 3.5.2004 Где: Dallas, US Репутация: 50 Всего: 172 |
Proxy
Прокси - это объект, который является "представителем" более сложного, трудносоздаваемого объекта. Например, соединение с БД - это пример паттерна Прокси. В каких случаях он нужен? Нужен он тогда, когда создание объекта занимает много времени (загрузка рисунка, открытие соединения с БД, с удаленным компьютером). Хороший пример - броузер ИЕ, пока рисунок не загружен, рисует стандартнуню иконку с красным квадратиком. Как только загрузка произошла, на этом месте появляется рисунок. Можно легко представить себе как написать такой прокси. Пример Java Код неполный, но дописать легко.
Преимущества Прокси упрощает работу с объектами, на взаимодействие с которыми может уйти много времени. В зависимости от задачи, прокси может выполнять функции кеширования, преобразования объекта. На этом структурные паттерны закончены. -------------------- |
|||
|
||||
Domestic Cat |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 5452 Регистрация: 3.5.2004 Где: Dallas, US Репутация: 50 Всего: 172 |
Chain of Responsibility
Как и многие другие паттерны, предназначен для того, чтобы объекты как можно меньше знали друг о друге. Пусть нам нужно обработать какое-либо событие, мессадж, попросту говоря, какой-либо объект. Смысл паттерна в том, чтобы передавать его от объекта к объекту в надежде, что кто-то да будет знать что с ним делать. Это очень напоминает несколько catch блоков - они идут от наиболее специфического к наиболее общему; нет гарантии что на выходе объект все-таки будет "пойман"; процессинг выполняетя последовательно; кетч-блоки ничего не знают друг о друге. Пример Java
Пример демонстрирует суть паттерна - мессаге - это "иксепшн", который прогоняется через 2 "кетч-блока". На любом этапе он может быть "пойман" и цепочка прервется. Преимущества Уменьшает связь между элементами цепи; таким образом приводит к структурированному, легко поддерживаемому и раширяемому коду. -------------------- |
|||
|
||||
Domestic Cat |
|
||||||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 5452 Регистрация: 3.5.2004 Где: Dallas, US Репутация: 50 Всего: 172 |
Command
Не понимаю я Комманд паттерн. Точнее понимаю, но зачем он нужен - непонятно. Поскольку он обычно применяется в ГУИ (по крайней мере в книжках), рассмотрим и мы его в таком контексте. Итак, есть фрейм (форма), на которой находится 20 кнопок. Вопрос: где обрабатывать нажатие этих 20 кнопок? Вариант 1: добавляем 20 листенеров (делегатов), все в классе фрейма (формы). Выглядит это не очень красиво, да и класс разрастается хуже некуда - 20 методов. Вариант 2 - все кнопки привязываем к одному методу, где отлавливаем источник нажатия и выполняем нужные действия. Этот вариант еще хуже, т.к. мы получим один гигантский метод-переросток с 20 ifами. Вариант номер 3 - сделать отдельный класс-делегат с 20 методами-слушателями. Результат тоже нехороший - отдельный, фактически ненужный класс. Здесь вроде бы и помогает Комманд. Пример С# Для начала пишем интерфейс
Теперь добавляем в класс формы единственный метод - делегат
Прикол весь в том, что теперь каждый баттон должен наследовать от интерфейса Command. То есть, нужно написать для каздой кнопки сабкласс от Button
Если все 20 кнопок делают чего-то свое, то нужно либо 20 сабклассов, либо 1 сабкласс, причем в Execute будут намешаны 20 ifов... Проблема в том, что мы этим ничего не выиграли, кроме того, что код стал "более правильным". Каждая кнопка отвечает сама за себя. Но работы гораздо больше - сколько сабклассов нужно написать! Учтите еще, что теперь просто так бросать кнопки на форму не получится - нужно написать UserControl и поместить его на тулбокс; и лишь затем делать гуи. Более ясной становится полезность паттерна если учесть, что в Command можно добавить другие методы, типа Undo / Redo. Тогда можно фиксировать выполненные Команды и при необходимости вызывать нужные методы. Тем не менее, это, имхо, не перевешивает недостатков паттерна. Преимущества Решайте сами. -------------------- |
||||||
|
|||||||
![]() ![]() ![]() |
Правила форума "Java" | |
|
Если Вам помогли, и атмосфера форума Вам понравилась, то заходите к нам чаще! С уважением, LSD, AntonSaburov, powerOn, tux, javastic. |
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей) | |
0 Пользователей: | |
« Предыдущая тема | Java: Общие вопросы | Следующая тема » |
|
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности Powered by Invision Power Board(R) 1.3 © 2003 IPS, Inc. |