Что такое паттерны проектирования и для чего их используют

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

Авторизуйтесь

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

Внимание! Материал был обновлён и разделён на три части. Предлагаем вам ознакомиться с ним по следующим ссылкам:

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

Что такое шаблоны проектирования

Шаблоны проектирования — это проверенные и готовые к использованию решения часто возникающих в повседневном программировании задач. Это не класс и не библиотека, которую можно подключить к проекту, это нечто большее. Шаблон проектирования, подходящий под задачу, реализуется в каждом конкретном случае. Кроме того, он не зависит от языка программирования. Хороший шаблон легко реализуется в большинстве, если не во всех языках, в зависимости от выразительных средств языка. Следует, однако, помнить, что такой шаблон, будучи примененным неправильно или к неподходящей задаче, может принести немало проблем. Тем не менее, правильно примененный шаблон поможет решить задачу легко и просто.

Существует три типа шаблонов:

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

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

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

Зачем нужны шаблоны проектирования

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

Пример

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

Просто, не правда ли? Давайте посмотрим поближе на шаблон «Стратегия».

Паттерн проектирования «Стратегия»

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

Где это можно использовать

Представьте, что вы разрабатываете класс, который может создать или обновить запись в базе данных. В обоих случаях входные параметры будут одни и те же (имя, адрес, номер телефона и т. п.), но, в зависимости от ситуации, он будет должен использовать различные функции для обновления и создания записи. Можно каждый раз переписывать условие if/else, а можно создать один метод, который будет принимать контекст:

Обычно шаблон «Стратегия» подразумевает инкапсуляцию алгоритмов в классы, но в данном случае это излишне. Помните, что вы не обязаны следовать шаблону слово в слово. Любые варианты допустимы, если они решают задачу и соответствуют концепции.

Шаблон «Адаптер»

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

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

Как его использовать?

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

Сравните два примера.

Без адаптера:

Если нам придется использовать такой код повторно, мы будем вынуждены переписывать все это заново.

С использованием адаптера:

Мы можем создать класс-обертку Account :

Теперь мы можем использовать класс Account каждый раз и, кроме того, мы можем добавить в него дополнительные функции.

Шаблон «Метод-фабрика»

Фабрика — порождающий шаблон, который представляет собой класс с методом для создания различных объектов.

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

Как его использовать?

Сначала создадим три класса:

Теперь мы можем написать нашу фабрику:

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

Шаблон «Декоратор»

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

Цель — в расширении поведения конкретного объекта без необходимости изменять поведение базового класса. Это позволит использовать несколько декораторов одновременно. Этот шаблон — альтернатива наследованию. В отличие от наследования, декоратор добавляет поведение в процессе выполнения программы.

Для реализации декоратора нам понадобится:

Как его использовать?

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

Сначала определимся, какие «декорации» нам нужны:

Теперь мы можем написать сами декораторы:

Теперь мы можем использовать их так:

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

Читайте также:  что такое стиль в контакте

Шаблон «Одиночка»

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

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

Как его использовать?

Теперь мы можем получить доступ к сессии из различных участков кода, даже из других классов. Метод getInstance всегда будет возвращать одну и ту же сессию.

Заключение

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

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

Источник

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

Автор материала, перевод которого мы публикуем, говорит, что, начиная проект, к написанию кода приступают не сразу. В первую очередь определяют цель и границы проекта, затем — выявляют те возможности, которыми он должен обладать. Уже после этого либо сразу пишут код, либо, если речь идёт о достаточно сложном проекте, подбирают подходящие паттерны проектирования, которые ложатся в его основу. Этот материал посвящён паттернам проектирования в JavaScript. Он рассчитан, преимущественно, на начинающих разработчиков.

Что такое паттерн проектирования?

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

Зачем нужны паттерны проектирования?

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

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

Например, если вы используете паттерн «Декоратор», это тут же сообщит новому программисту, пришедшему в проект, о том, какие именно задачи решает некий фрагмент кода и зачем он нужен. Благодаря этому такой программист сможет больше времени уделить практическим задачам, которые решает программа, а не попыткам понять её внутреннее устройство.

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

Паттерн «Модуль»

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

Модули — это составная часть любого современного JavaScript-приложения. Они помогают поддерживать чистоту кода, способствуют разделению кода на осмысленные фрагменты и помогают его организовывать. В JavaScript существует множество способов создания модулей, одним из которых является паттерн «Модуль» (Module).

В отличие от других языков программирования, JavaScript не имеет модификаторов доступа. То есть, переменные нельзя объявлять как приватные или публичные. В результате паттерн «Модуль» используется ещё и для эмуляции концепции инкапсуляции.

Этот паттерн использует IIFE (Immediately-Invoked Functional Expression, немедленно вызываемое функциональное выражение), замыкания и области видимости функций для имитации этой концепции. Например:

После того, как этот код будет выполнен, myModule будет выглядеть следующим образом:

Паттерн «Открытый модуль»

Паттерн «Открытый модуль» (Revealing Module) представляет собой немного улучшенную версию паттерна «Модуль», которую предложил Кристиан Хейльманн. Проблема паттерна «Модуль» заключается в том, что нам приходится создавать публичные функции только для того, чтобы обращаться к приватным функциям и переменным.

В рассматриваемом паттерне мы назначаем свойствам возвращаемого объекта приватные функции, которые хотим сделать общедоступными. Именно поэтому данный паттерн и называют «Открытый модуль». Рассмотрим пример:

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

После выполнения IIFE myRevealingModule выглядит так:

Рассмотрим преимущества паттерна «Открытый модуль» перед паттерном «Модуль»:

Модули в ES6

До выхода стандарта ES6 в JavaScript не было стандартного средства для работы с модулями, в результате разработчикам приходилось использовать сторонние библиотеки или паттерн «Модуль» для реализации соответствующих механизмов. Но с приходом ES6 в JavaScript появилась стандартная система модулей.

▍Экспорт модуля

Есть два способа экспорта функции или переменной, объявленной в модуле:

▍Импорт модуля

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

Читайте также:  что делать ночью на улице одному

▍Псевдонимы для экспортируемых и импортируемых сущностей

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

Для переименования сущностей при экспорте можно поступить так:

Для переименования сущностей при импорте используется такая конструкция:

Паттерн «Синглтон»

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

Фактически, то, что мы называем паттерном «Синглтон», имелось в JavaScript всегда, но называют это не «Синглтоном», а «объектным литералом». Рассмотрим пример:

Паттерн «Синглтон» можно реализовать с использованием функции-конструктора. Выглядит это так:

Паттерн «Синглтон» можно реализовать с использованием паттерна «Модуль». Например:

Паттерн «Фабрика»

Паттерн «Фабрика» (Factory) использует для создания объектов так называемые «фабричные методы». При этом не требуется указывать классы или функции-конструкторы, которые применяются для создания объектов.

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

Паттерн «Декоратор»

Паттерн «Декоратор» (Decorator) используется для расширения функционала объектов без модификации существующих классов или функций-конструкторов. Этот паттерн можно использовать для добавления к объектам неких возможностей без модификации кода, который ответственен за их создание.

Вот простой пример использования этого паттерна:

Рассмотрим теперь практический пример применения этого паттерна. Предположим, стоимость автомобилей зависит от их особенностей, от имеющихся у них дополнительных функций. Без использования паттерна «Декоратор» нам, для описания этих автомобилей, пришлось бы создавать разные классы для разных комбинаций этих дополнительных функций, в каждом из которых присутствовал бы метод для нахождения стоимости автомобиля. Например, это может выглядеть так:

После этого можно узнать стоимость автомобиля в улучшенной комплектации:

Итоги

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

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

Уважаемые читатели! Какими паттернами проектирования вы пользуетесь чаще всего?

Источник

Шаблоны проектирования простым языком. Часть первая. Порождающие шаблоны

Авторизуйтесь

Шаблоны проектирования простым языком. Часть первая. Порождающие шаблоны

Шаблоны проектирования — это руководства по решению повторяющихся проблем. Это не классы, пакеты или библиотеки, которые можно было бы подключить к вашему приложению и сидеть в ожидании чуда. Они скорее являются методиками, как решать определенные проблемы в определенных ситуациях.

Википедия описывает их следующим образом:

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

Будьте осторожны

Также заметьте, что примеры ниже написаны на PHP 7. Но это не должно вас останавливать, ведь принципы остаются такими же.

Типы шаблонов

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

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

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

Существуют следующие порождающие шаблоны:

Простая фабрика (Simple Factory)

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

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

Простыми словами: Простая фабрика генерирует экземпляр для клиента, не раскрывая никакой логики.

Перейдем к коду. У нас есть интерфейс Door и его реализация:

И затем мы можем использовать всё это:

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

Фабричный метод (Fabric Method)

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

Пример из жизни: Рассмотрим пример с менеджером по найму. Невозможно одному человеку провести собеседования со всеми кандидатами на все вакансии. В зависимости от вакансии он должен распределить этапы собеседования между разными людьми.

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

Читайте также:  Что такое признаки предметов в русском языке

17–19 декабря, Онлайн, Беcплатно

Перейдём к коду. Рассмотрим приведенный выше пример про HR-менеджера. Изначально у нас есть интерфейс Interviewer и несколько реализаций для него:

Теперь создадим нашего HiringManager :

И теперь любой дочерний класс может расширять его и предоставлять необходимого интервьюера:

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

Абстрактная фабрика (Abstract Factory)

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

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

Простыми словами: Фабрика фабрик. Фабрика, которая группирует индивидуальные, но связанные/зависимые фабрики без указания их конкретных классов.

Обратимся к коду. Используем пример про двери. Сначала у нас есть интерфейс Door и несколько его реализаций:

Затем у нас есть несколько DoorFittingExpert для каждого типа дверей:

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

Когда использовать: Когда есть взаимосвязанные зависимости с не очень простой логикой создания.

Строитель (Builder)

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

Пример из жизни: Представьте, что вы пришли в McDonalds и заказали конкретный продукт, например, БигМак, и вам готовят его без лишних вопросов. Это пример простой фабрики. Но есть случаи, когда логика создания может включать в себя больше шагов. Например, вы хотите индивидуальный сэндвич в Subway: у вас есть несколько вариантов того, как он будет сделан. Какой хлеб вы хотите? Какие соусы использовать? Какой сыр? В таких случаях на помощь приходит шаблон «Строитель».

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

Давайте я покажу на примере, что такое «Телескопический конструктор». Когда-то мы все видели конструктор вроде такого:

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

Затем мы берём «Строителя»:

Когда использовать: Когда может быть несколько видов объекта и надо избежать «телескопического конструктора». Главное отличие от «фабрики» — это то, что она используется, когда создание занимает один шаг, а «строитель» применяется при множестве шагов.

Прототип (Prototype)

Задаёт виды создаваемых объектов с помощью экземпляра-прототипа и создаёт новые объекты путём копирования этого прототипа. Он позволяет уйти от реализации и позволяет следовать принципу «программирование через интерфейсы». В качестве возвращающего типа указывается интерфейс / абстрактный класс на вершине иерархии, а классы-наследники могут подставить туда наследника, реализующего этот тип.

Пример из жизни: Помните Долли? Овечка, которая была клонирована. Не будем углубляться, главное — это то, что здесь все вращается вокруг клонирования.

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

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

Обратимся к коду. В PHP это может быть легко реализовано с использованием clone :

Затем он может быть клонирован следующим образом:

Также вы можете использовать волшебный метод __clone для изменения клонирующего поведения.

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

Одиночка (Singleton)

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

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

Простыми словами: Обеспечивает тот факт, что создаваемый объект является единственным объектом своего класса.

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

Прим. перев. Подробнее о подводных камнях шаблона одиночка читайте в нашей статье.

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

Источник

Сайт для любознательных читателей