Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Кочелаев_сессия.docx
Скачиваний:
3
Добавлен:
08.08.2019
Размер:
128.03 Кб
Скачать
  1. Паттерн Facade

Шаблон Facade (Фасад) — Шаблон проектирования, позволяющий скрыть сложность системы путем сведения всех возможных внешних вызовов к одному объекту, делегирующему их соответствующим объектам системы.

Проблема

Как обеспечить унифицированный интерфейс с набором разрозненных реализаций или интерфейсов, например, с подсистемой, если нежелательно высокое связывание с этой подсистемой или реализация подсистемы может измениться?

Решение

Определить одну точку взаимодействия с подсистемой — фасадный объект, обеспечивающий общий интерфейс с подсистемой и возложить на него обязанность по взаимодействию с её компонентами. Фасад — это внешний объект, обеспечивающий единственную точку входа для служб подсистемы. Реализация других компонентов подсистемы закрыта и не видна внешним компонентам. Фасадный объект обеспечивает реализацию паттерна Устойчивый к изменениям (Protected Variations) с точки зрения защиты от изменений в реализации подсистемы.

Особенности применения

Шаблон применяется для установки некоторого рода политики по отношению к другой группе объектов. Если политика должна быть яркой и заметной, следует воспользоваться услугами шаблона Фасад. Если же необходимо обеспечить скрытность и аккуратность (прозрачность), более подходящим выбором является шаблон Заместитель (Proxy).

  1. Flyweight

Приспособленец (англ. Flyweight) - это объект, представляющий себя как уникальный экземпляр в разных местах программы, но по факту не являющийся таковым.

Цель

Оптимизация работы с памятью, путем предотвращения создания экземпляров элементов, имеющих общую сущность.

Описание

Flyweight используется для уменьшения затрат при работе с большим количеством мелких объектов. При проектировании приспособленца необходимо разделить его свойства на внешние и внутренние. Внутренние свойства всегда неизменны, тогда как внешние могут отличаться в зависимости от места и контекста применения и должны быть вынесены за пределы приспособленца.

Flyweight дополняет паттерн Factory таким образом, что Factory при обращении к ней клиента для создания нового объекта ищет уже созданный объект с такими же параметрами, что и у требуемого, и возвращает его клиенту. Если такого объекта нет, то фабрика создаст новый.

  1. MVC

Model-view-controller (MVC, «Модель-представление-поведение», «Модель-представление-контроллер») — схема использования несколькихшаблонов проектирования, с помощью которыхмодель данныхприложения,пользовательский интерфейс и взаимодействие с пользователем разделены на три отдельных компонента так, что модификация одного из компонентов оказывает минимальное воздействие на остальные. Данная схема проектирования часто используется для построения архитектурного каркаса, когда переходят от теории к реализации в конкретной предметной области [1].

  1. Пассивная модель — модель не имеет никаких способов воздействовать на представление или контроллер, и используется ими в качестве источника данных для отображения. Все изменения модели отслеживаются контроллером и он же отвечает за перерисовку представления, если это необходимо. Такая модель чаще используется вструктурном программировании, так как в этом случае модель представляет просто структуру данных, без методов их обрабатывающих.

  2. Активная модель — модель оповещает представление о том, что в ней произошли изменения, а представления, которые заинтересованы в оповещении, подписываются на эти сообщения. Это позволяет сохранить независимость модели как от контроллера так и от представления.

Классической реализацией паттерна MVC принято считать версию именно с активной моделью.

Основная цель применения этой концепции состоит в разделении бизнес-логики (модели) от ее визуализации (представлениявида). За счет такого разделения повышается возможность повторного использования. Наиболее полезно применение данной концепции когда пользователь должен видеть те же самые данные одновременно в различных контекстах и/или с различных точек зрения. В частности, выполняются следующие задачи:

  1. К одной модели можно присоединить несколько видов, при этом не затрагивая реализацию модели. Например, некоторые данные могут быть одновременно представлены в виде электронной таблицы, гистограммы и круговой диаграммы.

  2. Не затрагивая реализацию видов, можно изменить реакции на действия пользователя (нажатие мышью на кнопке, ввод данных), для этого достаточно использовать другой контроллер.

  3. Ряд разработчиков специализируются только в одной из областей: или разрабатывают графический интерфейс или разрабатывают бизнес-логику. Поэтому возможно добиться, что программисты, занимающиеся разработкой бизнес-логики (модели), вообще не будут осведомлены о том, какое представление будет использоваться.

Концепция

Концепция MVC позволяет разделить данные, представление и обработку действий пользователя на три отдельных компонента:

  • Модель (англ. Model). Модель предоставляет знания: данные и методы работы с этими данными, реагирует на запросы, изменяя своё состояние. Не содержит информации, как эти знания можно визуализировать.

  • Представлениевид (англ. View). Отвечает за отображение информации (визуализация). Часто в качестве представления выступает форма (окно) с графическими элементами.

  • Контроллер (англ. Controller). Обеспечивает связь между пользователем и системой: контролирует ввод данных пользователем и использует модель и представление для реализации необходимой реакции.

Важно отметить, что как представление, так и контроллер зависят от модели. Однако модель не зависит ни от представления, ни от контроллера. Тем самым достигается назначение такого разделения: оно позволяет строить модельнезависимо от визуального представления, а также создавать несколько различных представлений для одной модели.

Для реализации схемы Model-View-Controller используется достаточно большое число шаблонов проектирования (в зависимости от сложности архитектурного решения), основные из которых НаблюдательСтратегия,Компоновщик[5].

Наиболее типичная реализация отделяет вид от модели, путем установления между ними протокола взаимодействия, используя аппарат событий (подписка/оповещение). При каждом изменении внутренних данных в модели, модель оповещает все зависящие от неё представления, и представление обновляется. Для этого используется шаблон проектирования наблюдатель. При обработке реакции пользователя, вид выбирает в зависимости от нужной реакции нужный контроллер, который обеспечит ту или иную связь с моделью. Для этого используется шаблон проектирования стратегия, или вместо этого может быть модификация с использованием шаблона проектирования команда. А для возможности однотипного обращения с подобъектами сложно-составного иерархического вида, может использоваться шаблон проектированияКомпоновщик. Кроме того, могут использоваться и другие шаблоны проектирования, например, фабричный метод, который позволит задать по умолчанию тип контроллера для соответствующего вида.

Но в объектно-ориентированном программировании используется активная модель MVC, где модель это не только совокупность кода доступа к данным и СУБД, а вся бизнес-логика. В свою очередь контроллеры должны избавляться от логики приложения (бизнес-логики). Таким образом Контроллер становится «тонким» и выполняет исключительно функцию связующего звена (glue layer) между отдельными компонентами системы.

Последнее время, большое внимание многие профессиональные разработчики, то есть инженеры-конструкторы программного обеспечения, уделяют рефакторинг кода, как методы его реструктурирования, призванные без изменения содержания (то есть функциональности и функциональной целостности) обеспечить решение задач минимизации сложности, готовности к изменениям (гибкости), прозрачности документирования и многих других актуальных аспектов конструирования. Но, к сожалению, многие забывают о необходимостимотивированности изменений, даже на уровне рефакторинга. Применение измерений, в частности, метрик, позволяет определить необходимость внесения таких изменений, проведения рефакторинга. И не потому что “так, наверное, будет всё же лучше, красивше...”, а потому, что в иерархии наследования из 10 поколений классов – 9 являются абстрактными (“из любви к искусству”), а на 10-м (в силу превышений сроков проекта, после того, как долго и “в кайф” создавали архитектурно-красивый код) “повешено” 20 (!) бизнес-методов. Вот это – действительно обоснованная причина для рефакторинга, который, даже с применением самых совершенных инструментальных средств, вместе с обдумыванием необходимости рефакторинга, а потом, иногда, и борьбой с его последствиями из-за несогласованности членов команды (а это уже жизнь, даже в самом идеальном коллективе, где люди понимают друг друга с полуслова) часто является просто тратой времени. Если применяется рефакторинг, но не применяются метрики – в подавляющем большинстве случаев, это отрицательно влияет на проект. И таких примеров работ, требующих применения измерений, но, к несчастью, игнорирующих их, можно приводить достаточно долго. Вы, наверняка, сами можете рассказать на эту тему много примеров из жизни.

Базы данных

Базы данных – одна из проблемных областей в рефакторинге. Многие программы туго связаны с определенными схемами баз данных. Это одна из причин непростого изменения базы данных. Другая причина – перемещение данных. Даже если Вы грамотно спроектировали Вашу систему для уменьшения зависимостей между структурой БД и объектной моделью программы, изменение структуры базы данных вынуждает разработчика перемещать данные из старой структуры в новую, а это длительный и нудный процесс.

Чтобы решить эту проблему, можно поставить некий промежуточный слой между объектной моделью и базой данных, который будет отвечать за связь между ними и изолировать изменения этих двух моделей. Т.е. если вы измените одну модель, другую менять не нужно будет. Нужно будет просто изменить промежуточный слой. Такой слой конечно усложнит программу, но придаст ей некоторую гибкость. Даже если не считать рефакторинга, это можеть быть полезно в случаях, когда мы не можем контролировать БД.

Но необязательно начинать с промежуточного слоя. Вы можете создать временный слой как часть объектной модели. Таким образом Вы получите хорошую возможность для изменений.

Объектные базы данных с одной стороны помогают, но с другой – затрудняют работу. Некоторые объектно-ориентированные БД могут автоматически перемещать данные из одной версии объекта в другую. Это уменьшает трату сил, но все еще занимает часть Вашего времени. Но если такой возможности автоматической миграции данных нет, Вам придется это делать руками, что опять же отнимает много сил. Поэтому здесь нужно быть осторожным: нужно семь раз подумать, прежде чем менять структуру классов. Вы можете легко изменять поведение класса, но при перемещении полей нужно быть осторожным. Нужно использовать временные поля, чтобы создать эффект того, что данные перемещены (но на самом деле они еще пока на месте). А когда Вы полностью уверены, где должны быть данные, Вы их перемещаете окончательно. При этом потребуется изменить только временные поля – это поможет избежать ошибок.

Изменение интерфейсов

Одно из преимуществ объектов в том, что они позволяют изменять реализацию программного модуля независимо от интерфейса. Т.е. Вы можете изменить что угодно в объекте, не волнуясь об остальных. Но! Измените интерфейс – и все полетит к чертям.

Есть множество приемов рефакторинга по изменению интерфейса. О них мы конечно будем говорить в дальнейших статьях. Один из таких приемов, например, – изменение имени метода в классе.

Вы без проблем можете изменить имя метода, если имеете доступ ко всему коду, который вызывает этот метод. Но если этот метод используется каким-то кодом, к которому Вы не имеете доступа, это проблема. Интерфейс, используемый таким кодом, можно назвать общим интерфейсом (т.е. интерфейс, доступный и используемый множеством пользователей, которых Вы, возможно, даже не знаете). Однажды создав такой интерфейс, Вы уже не можете его менять. Здесь уже нужно искать какой-то более сложный способ.

Говоря коротко, при изменении интерфейса Вам придется поддерживать обе его версии: старую и новую. По крайней мере до тех пор, пока все пользователи старого интерфейса не перейдут на новый. Можно сделать так, что старый интерфейс продолжит работать: например, пусть все методы старого интерфейса просто вызывают переименованные методы нового. Заметьте: именно вызывать новый метод из старого, а не копировать код старого метода в новый. Иначе этот код получится в итоге продублированным. При этом, если есть возможность, нужно отметить метод со старым именем устаревшим (в документации или прямо в коде) – так называемый deprecated-метод. Это даст возможность пользователям интерфейса знать, что метод устарел и побудить их к переходу на новый.

Это конечно решение, но это в принципе головная боль. Из всего сказанного выше следует, что общие интерфейсы лучше не создавать. А уж если и создаете – то максимально продумывайте его, ведь он создается скорее всего раз и навсегда. Это фундамент, который очень сложно менять потом. А иногда вообще невозможно (попробуйте изменить фундамент 9-ти этажного жилого дома – придется сносить дом).

Изменения проектных решений, которые сложно рефакторить

Можете ли Вы застраховать себя от ошибок в проектировании или задумать проект таким образом, что его рефакторить потом вообще не нужно будет? Я думаю, вряд ли, потому что изначально мы зачастую не обладаем полной информацией о том, что точно должно делать наше проектное решение.

Когда мы думаем над каким-то проектом, мы имеем в голове (или на листе бумаги… где угодно) несколько вариантов, которые можем использовать для создания этого проекта. И при этом мы должны спрашивать себя: а насколько сложно будет потом изменить эту схему вот на эту? Если кажется, что изменить будет просто, то создаем такое решение, котором может быть и не покрывает всех требований, но простое. А чем проще решение, тем его легче потом изменять. Но если мы не видим простых решений, придется покрепче и подольше подумать над этим (семь раз отмерь – один раз отрежь). Но сложных решений все-таки нужно избегать.

Когда мы не должны рефакторить вообще?

Бывают такие ситуации, в которых рефакторить не имеет смысла. Например, если код настолько запутанный, что его очень трудно понять, то его лучше написать заново. А если код еще к тому же имеет большое количество багов, который Вы не можете стабилизировать, то тут нет никаких сомнений, что легче будет его переписать.

Помните: код перед проведением рефакторинга должен работать!

Но есть и другой вариант: для большой системы – это выделение всего кода в отдельные модули и рефакторинг каждого модуля по отдельности. Особенно это касается старых систем.

Еще один случай, когда мы не должны рефакторить – если время завершения проекта подходит к концу (deadline). Если Вы не успеете закончить рефакторинг, Вы как бы залезете в долг. Вы должны будете завершить проект. И может это даже покажется Вам интересным занятием и сможете перенести этот долг. Но если этот долг окажется большим, это будет для Вас разорением.

Но с другой стороны, зачастую, нехватка времени означает, что над кодом нужно провести рефакторинг, чтобы потом этот код отнимал меньше времени.

А в следующий раз мы противопоставим рефакторинг и производительность.

До встречи!

ПРИЗНАКИ:

Повторяющийся код, длинный метод, сложность условий, зацикленность на примитивах, неприличная демонстрация, расплывшееся решение, альтернативные классы с различными интерфейсами, ленивый класс, большой класс, операторы выбора вариантов(запутанная логика + не гибкая система), комбинаторный взрыв(одно и тоже одинаковым способом с различным количеством данных), оригинальное решение (есть 2 решения в программе, но одно неустойчиво и оригинально. Слабо различимый повторяющийся код)

Составление методов

  • Выделение метода (Extract Method)

  • Встраивание метода (Inline Method)

  • Встраивание временной переменной (Inline Temp)

  • Замена временной переменной вызовом метода (Replace Temp with Query)

  • Введение поясняющей переменной (Introduce Explaining Variable)

  • Расщепление временной переменной (Split Temporary Variable)

  • Удаление присваиваний параметрам (Remove Assignments to Parameters)

  • Замена метода объектом методов (Replace Method with Method Object)

  • Замещение алгоритма (Substitute Algorithm)

Организация данных.

  • Самоинкапсуляция поля (Self Encapsulate Field)

  • Замена значения данных объектом (Replace Data Value with Object)

  • Замена значения ссылкой (Change Value to Reference)

  • Замена ссылки значением (Change Reference to Value)

  • Замена массива объектом (Replace Array with Object)

  • Дублирование видимых данных (Duplicate Observed Data)

  • Замена однонаправленной связи двунаправленной (Change Unidirectional Association to Bidirectional)

  • Замена двунаправленной связи однонаправленной (Change Bidirectional Association to Unidirectional)

  • Замена магического числа символической константой (Replace Magic Number with Symbolic Constant)

  • Инкапсуляция поля (Encapsulate Field)

  • Инкапсуляция коллекции (Encapsulate Collection)

  • Замена записи классом данных (Replace Record with Data Class)

  • Замена кода типа классом (Replace Type Code with Class)

  • Замена кода типа подклассами (Replace Type Code with Subclasses)

  • Замена кода типа состоянием/стратегией (Replace Type Code with State/Strategy)

  • Замена подкласса полями (Replace Subclass with Fields)

Модульность

Время компиляции.

Организация коллективной работы над проектом.

Грамотная организация исходного кода.

Повторное использование собственного и чужого кода.

Исключения.

  • Синхронные исключения могут возникнуть только в определённых, заранее известных точках программы. Так, ошибка чтения файла или коммуникационного канала, нехватка памяти — типичные синхронные исключения, так как возникают они только в операции чтения из файла или из канала или в операции выделения памяти соответственно.

  • Асинхронные исключения могут возникать в любой момент времени и не зависят от того, какую конкретно инструкцию программы выполняет система. Типичные примеры таких исключений: аварийный отказ питания или поступление новых данных.

  • Обработка с возвратом подразумевает, что обработчик исключения ликвидирует возникшую проблему и приводит программу в состояние, когда она может работать дальше по основному алгоритму. В этом случае после того, как выполнится код обработчика, управление передаётся обратно в ту точку программы, где возникла исключительная ситуация (либо на команду, вызвавшую исключение, либо на следующую за ней, как в некоторых старых диалектах языка BASIC) и выполнение программы продолжается. Обработка с возвратом типична для обработчиков асинхронных исключений (которые обычно возникают по причинам, не связанным прямо с выполняемым кодом), для обработки синхронных исключений она малопригодна.

  • Обработка без возврата заключается в том, что после выполнения кода обработчика исключения управление передаётся в некоторое, заранее заданное место программы, и с него продолжается исполнение.

  • Структурная (поддержка языком)/ неструктурная обработка (собственные функции). Блоки, которые выполнятся всегда

  • Проверяемые исключения (заранее говорим, что в теле функции такие исключения возможны)

Недостатки: снижается скорость работы, глупые обработчики проверяемых исключений, понимание кода снижается.

Целостность кода – рефакторинг есть не уменьшение функционала. При этом он не сколько расширяет его, сколько делает код более читаемым и т.п.