Главная страница
Навигация по странице:

  • Паттерны делятся на следующие категории

  • Фабричный метод (Factory Method) Интерпретатор (Interpreter) Шаблонный метод (Template Method) Адаптер (Adapter)

  • Абстрактная фабрика (Abstract Factory) Строитель (Builder) Прототип (Prototype) Одиночка (Singleton) Мост (Bridge)

  • Цепочка обязанностей (Chain of responsibility) Команда (Command) Итератор (Iterator) Посредник (Mediator) Хранитель (Memento)

  • Реализация

  • Композиция

  • Агрегация

  • Интерфейсы или абстрактные классы

  • Тема 3_2_ Основные шаблоны. Тема Основные шаблоны


    Скачать 331.38 Kb.
    НазваниеТема Основные шаблоны
    Дата04.04.2023
    Размер331.38 Kb.
    Формат файлаppt
    Имя файлаТема 3_2_ Основные шаблоны.ppt
    ТипРешение
    #1035795

    Тема 3.2. Основные шаблоны


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

    Классификация паттернов


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

    Классификация паттернов


    Например, в зависимости от сферы применения существуют такие паттерны как паттерны анализа, проектирования, тестирования, документирования, организации процесса разработки, планирования проектов и другие.
    В настоящее время наиболее популярными паттернами являются паттерны проектирования.

    Классификация паттернов


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

    Паттерны делятся на следующие категории:


    Архитектурные паттерны
    Паттерны проектирования
    Идиомы


    Архитектурные паттерны, являясь наиболее высокоуровневыми паттернами, описывают структурную схему программной системы в целом.
    В данной схеме указываются отдельные функциональные составляющие системы, называемые подсистемами, а также взаимоотношения между ними.
    Примером архитектурного паттерна является хорошо известная программная парадигма "модель-представление-контроллер" (model-view-controller - MVC).
    В свою очередь, подсистемы могут состоять из архитектурных единиц уровнем ниже.


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


    Идиомы, являясь низкоуровневыми паттернами, имеют дело с вопросами реализации какой-либо проблемы с учетом особенностей данного языка программирования.
    При этом часто одни и те же идиомы для разных языков программирования выглядят по-разному или не имеют смысла вовсе.


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


    В среде Java такой проблемы просто не существует, так как там используется автоматическая сборка мусора.


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

    Шаблоны бывают следующих трех видов:


      Порождающие.
      Структурные.
      Поведенческие.

      Если говорить простыми словами, то это шаблоны, которые предназначены для создания экземпляра объекта или группы связанных объектов.

    Паттерны классов описывают отношения между классами посредством наследования.
    Отношения между классами определяются на стадии компиляции.
    К таким паттернам относятся:
      Фабричный метод (Factory Method)
      Интерпретатор (Interpreter)
      Шаблонный метод (Template Method)
      Адаптер (Adapter)


    Другая часть паттернов - паттерны объектов описывают отношения между объектами.
    Эти отношения возникают на этапе выполнения, поэтому обладают большей гибкостью.


    К паттернам объектов относят следующие:
      Абстрактная фабрика (Abstract Factory)
      Строитель (Builder)
      Прототип (Prototype)
      Одиночка (Singleton)
      Мост (Bridge)
      Компоновщик (Composite)
      Декоратор (Decorator)
      Фасад (Facade)
      Приспособленец (Flyweight)
      Заместитель (Proxy)
      Цепочка обязанностей (Chain of responsibility)
      Команда (Command)
      Итератор (Iterator)
      Посредник (Mediator)
      Хранитель (Memento)
      Наблюдатель (Observer)
      Состояние (State)
      Стратегия (Strategy)
      Посетитель (Visitor)


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


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


    Один паттерн может иметь различные реализации, и чем чаще вы будете сталкиваться с этими реализациями, тем лучше вы будете понимать смысл паттерна.
    Не стоит использовать паттерн, если вы его не понимаете, даже если он на первый взгляд поможет вам в решении задачи.
    И в конечном счете надо придерживаться принципа KISS (Keep It Simple, Stupid) - сохранять код программы по возможности простым и ясным. Ведь смысл паттернов не в усложнении кода программы, а наоборот в его упрощении.


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


    Можно выделить несколько основных отношений:
      наследование, реализация, ассоциация, композиция, агрегация.

    Наследование


    Наследование является базовым принципом ООП и позволяет одному классу (наследнику) унаследовать функционал другого класса (родительского).
    Нередко отношения наследования еще называют генерализацией или обобщением.
    Наследование определяет отношение IS A, то есть "является".


    В данном случае используется наследование, а объекты класса Manager также являются и объектами класса User.


    С помощью диаграмм UML отношение между классами выражается в незакрашенной стрелочке от класса-наследника к классу-родителю:

    Реализация


    Реализация предполагает определение интерфейса и его реализация в классах.

    Реализация


    Например, имеется интерфейс IMovable с методом Move, который реализуется в классе Car:


    С помощью диаграмм UML отношение реализации также выражается в незакрашенной стрелочке от класса к интерфейсу, только линия теперь пунктирная:

    Ассоциация


    Ассоциация - это отношение, при котором объекты одного типа неким образом связаны с объектами другого типа.

    Ассоциация


    Например, объект одного типа содержит или использует объект другого типа. Например, игрок играет в определенной команде:
    Класс Player связан отношением ассоциации с класом Team.


    На схемах UML ассоциация обозначается в виде обычно стрелки:


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

    Композиция


    Композиция определяет отношение HAS A, то есть отношение "имеет".

    Композиция


    Например, в класс автомобиля содержит объект класса электрического двигателя:
    При этом класс автомобиля полностью управляет жизненным циклом объекта двигателя.
    При уничтожении объекта автомобиля в области памяти вместе с ним будет уничтожен и объект двигателя. И в этом плане объект автомобиля является главным, а объект двигателя - зависимой.


    На диаграммах UML отношение композиции проявляется в обычной стрелке от главной сущности к зависимой, при этом со стороны главной сущности, которая содержит, объект второй сущности, располагается закрашенный ромбик:

    Агрегация


    От композиции следует отличать агрегацию.
    Она также предполагает отношение HAS A, но реализуется она иначе:


    При агрегации реализуется слабая связь, то есть в данном случае объекты Car и Engine будут равноправны.
    В конструктор Car передается ссылка на уже имеющийся объект Engine.
    И, как правило, определяется ссылка не на конкретный класс, а на абстрактный класс или интерфейс, что увеличивает гибкость программы.


    Отношение агрегации на диаграммах UML отображается также, как и отношение композиции, только теперь ромбик будет незакрашенным:

    Общие рекомендации


    Вместо наследования следует предпочитать композицию.
    При наследовании весь функционал класса-наследника жестко определен на этапе компиляции.
    Во время выполнения программы его нельзя динамически переопределить.
    Класс-наследник не всегда может переопределить код, который определен в родительском классе.
    Композиция позволяет динамически определять поведение объекта во время выполнения, и поэтому является более гибкой.

    Общие рекомендации


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

    Интерфейсы или абстрактные классы


    Один из принципов проектирования гласит, что при создании системы классов надо программировать на уровне интерфейсов, а не их конкретных реализаций.
    Под интерфейсами в данном случае понимаются не только типы C#, определенные с помощью ключевого слова interface, а определение функционала без его конкретной реализации.
    Под данное определение попадают как интерфейсы, так и абстрактные классы, которые могут иметь абстрактные методы без конкретной реализации.
    При проектировании программ в паттернах можно заменять абстрактные классы на интерфейсы и наоборот.


    Когда следует использовать абстрактные классы:
      Если надо определить общий функционал для родственных объектов
      Если мы проектируем довольно большую функциональную единицу, которая содержит много базового функционал
      Если нужно, чтобы все производные классы на всех уровнях наследования имели некоторую общую реализацию. При использовании абстрактных классов, если мы захотим изменить базовый функционал во всех наследниках, то достаточно поменять его в абстрактном базовом классе.
      Если же нам вдруг надо будет поменять название или параметры метода интерфейса, то придется вносить изменения и также во всех классы, которые данный интерфейс реализуют.


    Когда следует использовать интерфейсы:
      Если нам надо определить функционал для группы разрозненных объектов, которые могут быть никак не связаны между собой.
      Если мы проектируем небольшой функциональный тип

      Ключевыми здесь являются первые пункты, которые можно свести к следующему принципу: если классы относятся к единой системе классификации, то выбирается абстрактный класс. Иначе выбирается интерфейс.

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


    Абстрактный класс Vehicle определяет абстрактный метод перемещения Move(), а классы-наследники его реализуют.
    Предположим, что система транспорта не ограничивается вышеперечисленными транспортными средствами.
    Например, можно добавить самолеты, лодки.
    Можно также добавить лошадь - животное, которое может также выполнять роль транспортного средства, дирижабль.
    Получается широкий круг объектов, которые связаны только тем, что являются транспортным средством и должны реализовать некоторый метод Move(), выполняющий перемещение.


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


    Теперь метод Move() определяется в интерфейсе IMovable, а конкретные классы его реализуют.
    Говоря об использовании абстрактных классов и интерфейсов можно привести еще такую аналогию, как состояние и действие.
    Как правило, абстрактные классы фокусируются на общем состоянии классов-наследников.
    В то время как интерфейсы строятся вокруг какого-либо общего действия.


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


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


    Например, в одной системе классов, которые наследуются от общего астрактного класса, были бы звезды, в том числе и солнце, планеты, астероиды и так далее - то есть все те объекты, которые могут иметь какое-то общее с солнцем состояние.
    В рамках другой системы классов мы могли бы определить электрические приборы, в том числе электронагреатель.


    И так, для каждой разноплановой сущности можно было бы составить свою систему классов, исходяющую от определенного абстрактного класса.
    А для общего действия определить интерфейс, например, IHeatable, в котором бы был метод Heat, и этот интерфейс реализовать во всех необходимых классах.


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


    https://metanit.com/sharp/patterns/1.1.php



    написать администратору сайта