JavaRush /Курсы /Architecture & Logic /Паттерны проектирования

Паттерны проектирования

Architecture & Logic
1 уровень , 0 лекция
Открыта

Знакомство с паттернами

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

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

Важно! Мы привыкли, что словом дизайн обычно подразумевают графический дизайн, в английском это не так. Английское слово design по смыслу ближе к к слову «проектирование» и/или «устройство». Например, дизайн двигателя — это не его внешний вид, а его внутреннее устройство.

Поэтому design pattern — это именно паттерн/шаблон проектирования. Рекомендую вообще перестать употреблять слово дизайн в смысле «внешний вид». Вы — будущий Software Engineer, и для вас дизайн — это именно проектирование.

Так что же такое этот design pattern? В первую очередь паттерн проектирования — это стандартное решение стандартной проблемы. Хорошее, эффективное и проверенное временем решение.

Допустим, вам задали спроектировать велосипед, вы можете сделать ему два колеса, три или даже пять. Так, кстати, на заре проектирования и было. Но проверенный временем подход — два колеса. А ведь к нынешнему очевидному подходу шли через боль и ошибки:

Эволюция велосипеда

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

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

История появления паттернов проектирования

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

Для организации работы большой группы людей использовали практики и подходы из сферы строительства. Кстати, именно оттуда в программирование пришли такие термины как сборка (build), Software Developer (строитель), и понятие архитектуры.

И как вы уже догадываетесь, идею design pattern тоже почерпнули из сферы строительства. Концепцию паттернов впервые описал Кристофер Александер в книге «Язык шаблонов. Города. Здания. Строительство». В этой книге для описания процессов проектирования городов был использован специальный язык — паттерны.

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

Поэтому ничего удивительного, что в 1994 году вышла книга «Приемы объектно-ориентированного проектирования. Паттерны проектирования», в которую вошли 23 паттерна, решающие различные проблемы объектно-ориентированного дизайна.

Книгу написали 4 автора: Эрих Гамма, Ричард Хелм, Ральф Джонсон и Джон Влиссидес. Название книги было слишком длинным, чтобы кто-то смог его запомнить. Поэтому вскоре все стали называть её «book by the gang of four», то есть «книга от банды четырех», а затем и вовсе «GoF book».

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

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

Python-way: почему нельзя просто скопировать код из Java/C++?

Классическая книга «Банды четырёх» (GoF) — это библия проектирования. Но есть нюанс: она была написана в 1994 году с упором на языки C++ и Smalltalk. Многие проблемы, которые эти паттерны решали в C++, в Python либо отсутствуют, либо решаются штатными средствами языка.

Если вы попробуете найти в Интернете примеры паттернов на Python, вы часто наткнетесь на туториалы, где авторы механически переписывают Java-код на Python. Там будут абстрактные фабрики фабрик, интерфейсы, которые ничего не делают, и классы, единственная задача которых — хранить одну функцию.

Важно! Слепое копирование классических реализаций в Python — это антипаттерн. Такой код называют «Java-style Python». Он работает, но его сложно читать, тестировать и поддерживать.

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

  • Singleton (Одиночка) в C++ это сложный класс с контролем единственного экземпляра. В Python любой импортируемый модуль — это уже Singleton по дизайну языка. Нам не нужно писать class Singleton, мы просто создаем объект в файле settings.py.
  • Strategy (Стратегия) в старых языках для этого нужно создавать интерфейс и кучу классов-наследников. В Python мы можем просто передать функцию как аргумент в другую функцию.
  • Decorator (Декоратор) в Python это вообще встроенный синтаксис языка @login_required, а не просто архитектурный прием.
  • Builder (Строитель) часто заменяется грамотным использованием именованных аргументов kwargs в конструкторе класса и значениями по умолчанию.

Поэтому в этом курсе мы будем учиться не просто «зубрить схемы», а понимать суть проблемы. Если проблему можно решить стандартными средствами Python (списковыми включениями, декораторами, контекстными менеджерами), мы будем делать именно так. А к «тяжелой артиллерии» классических паттернов будем прибегать там, где это действительно необходимо для архитектуры.

Зачем знать паттерны и какими они бывают

Многие программисты за всю жизнь не изучили ни одного паттерна, что, впрочем, не мешает им их применять. Как мы уже говорили раньше, паттерны — это хорошие проверенные временем решения и, если программист не дурак, то с опытом сам находит такие решения.

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

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

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

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

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

Самые низкоуровневые и простые паттерны — идиомы. Они не универсальны, поскольку применимы только в рамках одного языка программирования.

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

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

  • Порождающие паттерны (Creational) — заботятся о гибком создании объектов без внесения в программу лишних зависимостей. Они отвечают на вопрос: «Как создать объект, чтобы не запутать код?».
  • Структурные паттерны (Structural) — показывают различные способы построения связей между объектами. Отвечают на вопрос: «Как собрать классы в единую рабочую структуру?».
  • Поведенческие паттерны (Behavioral) — заботятся об эффективной коммуникации между объектами. Отвечают на вопрос: «Кто и за что отвечает, и как объекты общаются?».

Знакомство с UML

Начнем с 23 паттернов из книги «Банды четырёх» (GoF). Сами паттерны не привязаны к конкретному языку, поэтому для их описания используется стандарт UML (Unified Modeling Language).

В Python мы редко рисуем детальные UML-диаграммы перед написанием кода (мы предпочитаем писать прототипы), но читать их нужно уметь. Это «чертежи», на которых показано, как классы зависят друг от друга.

Типы связей в UML

classDiagram
        %% 1. Композиция (Сильная связь)
        Order *-- OrderItem : Композиция
        note for Order "Целое. Если удалить Заказ, строки тоже исчезнут."

        %% 2. Агрегация (Слабая связь)
        Department o-- User : Агрегация
        note for Department "Целое. Если закрыть Отдел, Сотрудники остаются."

        %% 3. Наследование (Обобщение)
        User <|-- Admin : Наследование (Is-A)
        class Admin{
            +delete_user()
        }

        %% 4. Зависимость
        ReportGenerator ..> Order : Зависимость (Uses)
        note for ReportGenerator "Генератор временноиспользует Заказ,чтобы создать PDF."
  

Разберем эти стрелки подробнее, от сильных связей к слабым:

  • Наследование (сплошная линия с треугольником) классическое ООП. Admin является User. Он получает все его свойства и методы.
  • Композиция (черный ромб) связь «Часть-Целое» (смертельная). OrderItem не имеет смысла без Order. В Django это часто реализуется через on_delete=models.CASCADE.
  • Агрегация (белый ромб): связь «Часть-Целое» (свободная). User работает в Department. Но если отдел расформируют, юзер не удаляется из базы, он просто меняет отдел. В Django это on_delete=models.SET_NULL.
  • Зависимость (Пунктирная стрелка) самая слабая связь. Класс ReportGenerator не хранит внутри себя заказ. Он просто получает его как аргумент метода, берет данные и «забывает».

Полный список паттернов

Виды паттернов будем обозначать разными цветами и буквами:

B — поведенческие (behavioral);

C — порождающие (creational);

S — структурные (structural).

И наконец список из 23-х паттернов проектирования:

C — Абстрактная фабрика

S — Адаптер

S — Мост

C — Строитель

B — Цепочка обязанностей

B — Команда

S — Компоновщик

S — Декоратор

S — Фасад

C — Фабричный метод

S — Приспособленец

B — Интерпретатор

B — Итератор

B — Посредник

B — Хранитель

C — Прототип

S — Прокси

B — Наблюдатель

C — Одиночка

B — Состояние

B — Стратегия

B — Шаблонный метод

B — Посетитель

Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ