Повышаем свою стоимость: Паттерны проектирования

Повышаем свою стоимость: Паттерны проектирования

Больше вкусностей найдешь на моем канале - https://t.me/emotional_robot


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

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


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

Об этом, разумеется, задумались давно. Есть группа авторов под названием "Банда четырех", в которую входят Эрих Гамма, Ричард Хелм, Ральф Джонсон, Джон Влиссидес. Они как-то собрались за одним столом с водочкой и огурчиками, выпили, потрещали за жизнь и решили, что кругом творится какая-то дичь и программисты творят, что хотят. Почему бы не составить огромный список готовых паттернов (или шаблонов, шо как бэ перевод для слова "pattern") проектирования, чтобы кодеры-новички не хреначили абы что, только-только разобравшись с ООП ЯПом, а придерживались определенных правил. Да и вообще, когда программисты пишут примерно одинаковый код, то текучка кадров не так страшна: любой новый опытный кодер, увидев код, в котором использовались паттерны, быстро в него вникнет и начнет работу.

Таки "Банда четырех" забились собрать в одну книгу все адекватные существующие решения, и получилась "Design Patterns". Огромная, наполненная непонятными вещами с примерами кода на С++, отпугивающая любого заинтересовавшегося программиста - новичка. Но это только поначалу так кажется. Скажу так: это толстая шпаргалка, которую можно первый раз поверхностно пробежать, запомнить названия всех паттернов и примерно понимать, зачем они нужны, дабы потом, когда перед вами встала задача определенного вида, вы сразу сообразили, какой паттерн здесь можно применить и открыли нужную главу книги.

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

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

Но мы можем вкратце пробежаться по трем группам этих паттернов.

Порождающие паттерны


Надеюсь, вы помните разговор об ООП? Давайте уже домучаем до конца нашего бедного бомжика:

class Homeless {

nickname: String; // "кликуха" с типом данных "строка"

age: Number; // возраст с типом данных "число"

}

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

const homelessKompot = new Homeless("Kompot", 55)

Собственно, в данной строчке мы говорим, что хотим создать объект "homelessKompot" с именем "Kompot" и возрастом 55 лет. Чтобы класс смог это сделать, добавим ему конструктор:

class Homeless {

private _nickname: String; // сделаем поле приватным

private _age: Number; // и это тоже

constructor(nickname: String, age: Number) {

this._nickname = nickname;

this._age = age;

}

}

То есть, конструктор класса принимает в качестве аргументов данные, которые мы отдаем ему при вызове со словом "new", и записывает их в свои приватные поля. Дальше можно написать различные методы, которые манипулируют этими данными. Самое главное, что нужно запомнить - вся движуха порождающих паттернов крутится вокруг конструктора класса.

Порождающих паттернов меньше всего: Singleton, Prototype, Factory Method, Abstract Factory, Builder. Однако, замечу, что со времен написания книги "Бандой четырех" прошло дофигища времени, и появились новые паттерны. Но до новых паттернов еще добраться нужно, тут основные бы осилить. Поэтому не заморачивайтесь и разбирайте основные сначала.

В качестве примера разберем самый простой паттерн - Singleton. Суть его в том, что нам иногда, по разным причинам, необходимо иметь только один экземпляр класса (собственно, потому он и называется "Одиночка"). Чтобы это стало возможным, нам нужно сделать конструктор класса приватным, а также добавить метод получения экземпляра класса, который либо создаст его при первом вызове, либо будет возвращать уже созданный:

class SingletonHomeless ("Бомж-одиночка", хоть фильм снимай) {

private instance: SingletonHomeless = null;

private constructor {}

public getInstance() {

if (instance == null) {

this.instance = new SingletonHomeless();

}

return this.instance;

}

}

Зачем делать конструктор приватным? Чтобы никто не смог вызвать его напрямую через "new" и получить другой экземпляр класса. Экземпляр должен быть один, и получить его можно только через специальный метод.

Структурные паттерны


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

На помощь приходят структурные паттерны. Как говорит любовь и спасительница всех студентов по имени Вики:

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

Структурных паттернов побольше: Adapter, Bridge, Composite, Decorator, Facade, Flyweight, Proxy.

Простой пример адаптера:

class Adapter implements IAdapter {

public getFromExternalLibrary() {

return externalLibrary.getSomething();

}

}

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

Другая, но похожая причина использования адаптера - несовместимость работы кода внешней библиотеки и вашего кода. Вам придется делать вызовы из внешней либы, внутри адаптера как-то преобразовывать полученный результат, а затем отдавать его вашей программе. Собственно, здесь конкретно видно, почему данный паттерн называется "Адаптер".

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

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


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

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

Я думаю, самый яркий пример паттерна, это "State", потому что вокруг состояния крутится популярная у фронтов библиотека React. Понятно, что сейчас приходится писать в функциональном стиле из-за хуков, но раньше компоненты на React писались в виде классов, а внутри классов использовался "state". В зависимости от текущего состояния изменялось поведение компонента, что отражалось на UI. Собственно, в этом смысл паттерна "State".

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

Итого

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



Report Page