Share
Embed
Структуры данных - лекция: шаблоны проектирования
Лекция курса "Структуры данных"
Архитектура приложения, проектирование приложения: шаблоны (паттерны) проектирования в стиле ООП
- Объектно-ориентированное программирование (ООП) Object oriented programming (OOP) vs Объектно-ориентированное проектирование (дизайн) (ООП/ООД) Object oriented design (OOD)
- Объектно-ориентированное проектирование
- Шаблоны проектирования
- Книги
- Одиночка Singleton [синглтон]
- Фабрика Factory [фактори]
- Наблюдатель Observer (Слушатель событий Event listener)
- Модель-Представление Model-View (MV)
- Модель-Представление-Контроллер Model-View-Controller (MVC)
- Работа с базами данных
- Еще шаблоны (из Э. Гаммы)
https://vk.com/video53223390_456239471
Anton
January 11, 2025
Transcript
59 slides
-
• Объектно-ориентированное программирование (ООП) Object oriented programming (OOP) • Объектно-ориентированное
проектирование (дизайн) (ООП/ООД) Object oriented design (OOD) • OOP vs OOD (на английском языке аббревиатуры отличаются, на русском — совпадают) -
Объектно-ориентированное проектирование • Объектная модель приложения: описываем предметную область языком
объектов и связей между ними • Логика исполнения — задача №2 (вторая после того, как сделали модель) • Развитие ОО-интуиции практикой -
Шаблоны проектирования • Люди не первый год проектируют приложения в
терминах ООП, за это время успели накопить некоторый опыт • Предметные области разные, а внутренняя кухня схожая • Не все можно (или удобно) реализовать в виде библиотеки • Типовые рекомендации к проектированию архитектуры приложения • Шаблоны (паттерны) проектирования • англ.: design patterns -
Примеры • Фабрика (Factory) • Одиночка (Singleton) • Слушатель событий
(Event listener) • Модель/представление (Model/View) • Модель/представление/контроллер (Model/View/Controller - MVC) -
Книги • Приемы объектно-ориентированного проектирования. Паттерны проектирования, Эрих Гамма, Ричард
Хелм, Ральф Джонсон, Джон Влиссидес, 1994 (регулярно переиздаётся и допечатывается) оригинал: Design Patterns: Elements of Reusable Object-Oriented Software • Паттерны проектирования, Эрик Фримен, Элизабет Фримен, Кэтти Сьерра, Берт Бейтс, 2004 (регулярно переиздаётся и допечатывается) оригинал: Head First Design Patterns: A Brain-Friendly Guide • Интернет, Хабр, Википедия -
Например: ConfigOptions • getOptX • getOptY • setOptX • setOptY
• Чтобы загружалось из файла (settings.cfg) или СУБД • И сохранялось туда же -
public class ConfigOptions() { private int optX; private String optY;
public int getOptX() { return optX; } public String getOptY() { return optY; } public void setOptX(int newVal) { this.optX = newVal; } public void setOptY(String newVal) { this.optY = newVal; } } -
Варианты • Создавать объект ConfigOptions везде, где обращаемся к настройкам
- во-первых, это некрасиво - можем получить всякие неприятные конфликты при сохранении файла из разных мест • Сделать все поля и методы статическими (static) - плохой тон • Применить шаблон «Одиночка» -
Шаблон «Одиночка» • Ровно 1 экземпляр класса на всё приложение
• Доступ к этому экземпляру из любой точки приложения -
public class ConfigOptions() { private static ConfigOptions instance; private ConfigOptions()
{} public static ConfigOptions getInstance() { if(instance == null) { instance = new ConfigOptions(); } return instance; } [...] public int getOptX() { […] } public String getOptY() { […] } [...] } -
Замечание (для Java) • Существует ненулевая вероятность, что предложенный (прямолинейный)
вариант реализации создаст два экземпляра ConfigOptions в многопоточном приложении (в instance останется последний) • Поэтому обычно используют немного другой вариант инициализации instance — в блоке инициализации static -
public class ConfigOptions() { private static ConfigOptions instance; private ConfigOptions()
{} public static ConfigOptions getInstance() { if(instance == null) { instance = new ConfigOptions(); } return instance; } [...] public int getOptX() { […] } public String getOptY() { […] } [...] } -
public class ConfigOptions() { private static ConfigOptions instance; private ConfigOptions()
{} static { instance = new ConfigOptions(); } public static ConfigOptions getInstance() { return instance; } [...] public int getOptX() { […] } public String getOptY() { […] } [...] } -
Например: темы виджетов • Виджеты (элементы управления): Button (кнопка), Label
(текст), Switch (переключатель) • Темы (внешний вид): Native (стиль операционной системы), Fancy (симпатичный) -
Button btn1 = new NativeButton(); Button btn2 = new FancyButton();
Label lbl1 = new FancyLabel(); Label lbl2 = new FancyLabel(); Switch sw1 = new NativeSwitch(); -
Какая здесь тема оформления? Как её сменить? • Нужен способ
создавать объекты-виджеты по месту • Мы не должны знать/выбирать тему (конкретную реализацию) в месте создания виджета -
Дополнительно • Возможность указать тему в настройках (одно название темы
определяет оформление сразу всех виджетов) • Загрузка новой темы с выбором произвольной темы из настроек (хотя бы) при старте приложения -
• interface WidgetFactory - createButton() : Button - createLable() :
Label - createSwitch() : Switch -
class NativeWidgetFactory implements WidgetFactory { public Button createButton() { return
new NativeButton(); } [...] } class FancyWidgetFactory implements WidgetFactory { public Button createButton() { return new FancyButton(); } [...] } -
WidgetFactory wfact = new NativeWidgetFactory(); //WidgetFactory wfact = new FancyWidgetFactory();
Button btn1 = wfact.createButton(); Button btn2 = wfact.createButton(); Label lbl1 = wfact.createLabel(); Label lbl2 = wfact.createLabel(); Switch sw1 = wfact.createSwitch(); -
WidgetFactory wfact = ConfigOptions.getInstance().getWidgetFactory(); Button btn1 = wfact.createButton(); Button btn2
= wfact.createButton(); Label lbl1 = wfact.createLabel(); Label lbl2 = wfact.createLabel(); Switch sw1 = wfact.createSwitch(); -
Задание • Добавьте настройки ConfigOptions в одну из лаб •
Например: игра «кто быстрее» — до скольки считать, какая задержка между удалениями введенных чисел • Или: лаба с сетевым графиком -
MyButton • Надпись («сохранить в файл») • Нарисовать (draw) -
написать текст (this.надпись) • Обработать нажатие - выполнить действие (сохранить в файл) -
У нас есть кнопка «сохранить в файл» • Хотим добавить
еще одну кнопку: «отправить в интернет» -
Как сделать так, чтобы разные экземпляры кнопки выполняли разные действия?
• Выполнять разные участки внутри MyButton по условию (совсем плохо) • Абстрактный onClick (получше, но не всегда удобно) • Вынести обработчик событий за пределы класса: добавить наблюдателя EventListener -
• btn1 = new MyButton(«Сохранить в файл»); btn1.setOnClickListener(new OnClickListener() {
void onClick() { // сохраняем в файл } } • btn2 = new MyButton(«Отправить в интернет»); btn2.setOnClickListener(new OnClickListener() { void onClick() { // отправляем в интернет } } -
MyButton • Надпись («сохранить в файл») • Нарисовать (draw) -
написать текст (this.надпись) • Обработать нажатие - выполнить действие (сохранить в файл) - clickListener.onClick(); • слушатель OnClickListener clickListener setOnClickListener(OnClickListener cl): clickListener = cl -
Много слушателей (как вариант) • OnClickListener listeners[]; или • javax.swing.event.EventListenerList
listeners; • addOnClickListener(OnClickListener cl) • Обработать нажатие: пробежать по всем слушателям listeners в цикле -
ru.wikipedia.org/wiki/Model-View-Controller • Концепция MVC была описана Трюгве Реенскаугом в 1978
году, работавшем в научно- исследовательском центре «Xerox PARC» над языком программирования «Smalltalk». Позже, Стив Бурбек реализовал шаблон в Smalltalk-80. • Окончательная версия концепции MVC была опубликована лишь в 1988 году в журнале Technology Object. -
• Хотим хранить данные, не думая о том, как их
будем отображать • Или вообще их отображать будем не мы • Или мы хотим отображать одни и те же данные разными способами • Но тот, кто будет их отображать, должен иметь возможность получить всю необходимую информацию для отрисовки • И оперативно получать информацию в том случае, если модель без его ведома (из другого представления или по своему внутреннему усмотрению) изменится (через паттерн Наблюдатель) -
SwitchModel • Boolean enabled • switchOn(вкл) enabled = true fireEnabledChange();
• switchOff(выкл) enabled = false fireEnabledChange(); • switch() (переключить) if(enabled) switchOff() else switchOn() • private SwitchModelListener listeners[] • addModelListener (lis) • fireEnabledChange for ml : listeners: ml.onEnabledChange -
SwitchView • sm: SwitchModel • Рисовать if sm.isEnabled() рисовать ВКЛ
else рисовать ВЫКЛ • SwitchModelListener sml - onEnabledChage: нужно перерисовать • onMouseClick sm.switch() -
Кстати • Если модель вдруг регулярно меняется, но слушатель событий
не предусмотрен, • можно организовать старый добрый poll (периодически опрашивать) -
MV + Controller • К обычной Model-View добавляем еще один
слой — логика обработки действий пользователя выделяется из представления в Контроллер • Влияем на модель: Представление → Контроллер → Модель -
Например: «сохранить в файл» • SaveButton.onClick: model.save() • Теперь хотим
это действие выполнять из меню, кнопкой на панели инструментов, сочетанием Ctrl+S и по таймеру • Чтобы не копировать код, можно вынести его в отдельное место: • Controller:saveModel(): model.save() • Дальше во всех перечисленных местах: controller.saveModel() • Логика в контроллере, конечно, может быть посложнее • Не обязательно выносить в отдельный класс -
Java+Swing • javax.swing.JTable + TableModel (DefaultTableModel) • javax.swing.JTree + TreeModel
(DefaultTreeModel) • javax.swing.JList + ListModel (DefaultListModel) • и т.п. -
Андроид • Представление (вид) - файл xml (разметка, кода нет)
• Файл с кодом (контроллер) - Activity, Fragment или Component - загружает вид из XML - подключает обработчиков событий - взаимодействует с моделью • Модель: ваш код, инфраструктура приложения, источники данных для списков, слой взаимодействия с СУБД (DAO/ORM и т.п) и т.п. -
Веб • Динамические странички HTML (php, jsp, ...) - Плохо:
генерация кода HTML вперемешку с логикой запроса к СУБД (всё кусками в одном файле) - Получше: использовать генераторы шаблонов страниц (вынести представление в отдельный файл) • ReactJS: MVC на уровне архитектуры (отображение в браузере на клиенте, логика и данные — на сервере) -
Работа с базами данных • DAO — Data access object
(паттерн) • ORM (класс фреймворков, реализующих паттерн как библиотека) -
Задание: Сетевой график - MVC • GraphModel - вершины -
ребра - связать() - разорватьСвязь() … - события • GraphView (представление в виде вершин и ребер) • TableView (представление в виде таблицы) -
• Abstract Factory (абстрактная фабрика) (93) Предоставляет интерфейс для создания
семейств, связанных между собой, или независимых объектов, конкретные классы которых неизвестны. • Adapter (адаптер) (141) Преобразует интерфейс класса в некоторый другой интерфейс, ожидаемый клиентами. Обеспечивает совместную работу классов, которая была бы невозможна без данного паттерна изза несовместимости интерфейсов. • Bridge (мост) (152) Отделяет абстракцию от реализации, благодаря чему появляется возможность независимо изменять то и другое. • Builder (строитель) (103) Отделяет конструирование сложного объекта от его представления, позволяя использовать один и тот же процесс конструирования для созданияразличных представлений. -
• Chain of Responsibility (цепочка обязанностей) (217) Можно избежать жесткой
зависимости отправителя запроса от его получателя, при этом запросом начинает обрабатываться один из нескольких объектов. Объектыполучатели связываются в цепочку, и запрос передается по цепочке, пока какойто объект его не обработает. • Command (команда) (226) Инкапсулирует запрос в виде объекта, позволяя тем самым параметризовывать клиентов типом запроса, устанавливать очередность запросов, протоколировать их и поддерживать отмену выполнения операций. • Composite (компоновщик) (162) Группирует объекты в древовидные структуры для представления иерархий типа «частьцелое». Позволяет клиентам работать с единичными объектами так же, как с группами объектов. • Decorator (декоратор) (173) Динамически возлагает на объект новые функции. Декораторы применяются для расширения имеющейся функциональности и являются гибкой альтернативой порождению подклассов. -
• Facade (фасад) (183) Предоставляет унифицированный интерфейс к множеству интерфейсов
в некоторой подсистеме. Определяет интерфейс более высокого уровня, облегчающий работу с подсистемой. • Factory Method (фабричный метод) (111) Определяет интерфейс для создания объектов, при этом выбранный класс инстанцируется подклассами. • Flyweight (приспособленец) (191) Использует разделение для эффективной поддержки большого числа мелких объектов. • Interpreter (интерпретатор) (236) Для заданного языка определяет представление его грамматики, а также интерпретатор предложений языка, использующий это представление. • Iterator (итератор) (173) Дает возможность последовательно обойти все элементы составного объекта, не раскрывая его внутреннего представления. -
• Mediator (посредник) (263) Определяет объект, в котором инкапсулировано знание
о том, как взаимодействуют объекты из некоторого множества. Способствует уменьшению числа связей между объектами, позволяя им работать без явных ссылок друг на друга. Это, в свою очередь, дает возможность независимо изменять схему взаимодействия. • Memento (хранитель) (272) Позволяет, не нарушая инкапсуляции, получить и сохранить во внешней памяти внутреннее состояние объекта, чтобы позже объект можно было восстановить точно в таком же состоянии. • Observer (наблюдатель) (281) Определяет между объектами зависимость типа одинкомногим, так что при изменении состоянии одного объекта все зависящие от него получают извещение и автоматически обновляются. • Prototype (прототип) (121) Описывает виды создаваемых объектов с помощью прототипа и создает новые объекты путем его копирования. -
• Proxy (заместитель) (203) Подменяет другой объект для контроля доступа
к нему. • Singleton (одиночка) (130) Гарантирует, что некоторый класс может иметь только один экземпляр, и предоставляет глобальную точку доступа к нему. • State (состояние) (291) Позволяет объекту варьировать свое поведение при изменении внутреннего состояния. При этом создается впечатление, что поменялся класс объекта. • Strategy (стратегия) (300) Определяет семейство алгоритмов, инкапсулируя их все и позволяя подставлять один вместо другого. Можно менять алгоритм независимо от клиента, который им пользуется. -
• Template Method (шаблонный метод) (309) Определяет скелет алгоритма, перекладывая
ответственность за некоторые его шаги на подклассы. Позволяет подклассам переопределять шаги алгоритма, не меняя его общей структуры. • Visitor (посетитель) (314) Представляет операцию, которую надо выполнить над элементами объекта. Позволяет определить новую операцию, не меняя классы элементов, к которым он применяется.