Построй свой код: шаблоны проектирования

Все люди делятся на правильных и неправильных. Главное, что нормальные программисты, и правильные, и нет, используют в своих проектах Шаблоны Проектирования, а не лепят все как хотят от балды.

Как организовать сложный проект на PHP, чтобы его можно было легко доработать, сменить ему дизайн, добавить пару модулей и возможность экспортировать данные в любом формате, который придумают всякие умные люди (например, WML, RSS (и вообще XML), JSON, ...)? Налепить кучу страничек и сделать в каждом include 'include.php'? Выглядит разумно, но недостатков куча, например как организовать особый экспорт данных - придется ведь приделывать новую страничку для каждого формата или устраивать кучу if'ов (case'ов). Подумай, во что превратится твоя крутейшая CMS после кучи таких добавлений?

Это - типичная задача проектирования компьютерных программ. Какую бы прогу ты не писал, если она представляет собой нечто большее, чем 'Hello World!', тебе придется задуматься о ее построении - это и называется проектированием. И чтобы не напрягать излишне свой и без того напряженный мозг, люди строят программы по типовым "чертежам", которые называются шаблоны проектирования.

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

Шаблон функционального дизайна.

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

Обеспечить такой дизайн достаточно просто - надо к каждой функции (процедуры тоже функции, что бы там не придумывал этот Вирт) приписывать комментарий, что она делает. Если в описании будет слово "и" или "или" ("эта функция стирает носки и готовит кофе"), значит, стоить разбить ее на две ("эта функция стирает носки", "эта функция готовит кофе").

Шаблон Заместитель (Proxy)

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

Реализуется это просто: создается общий интерфейс для замещаемого класса и для заместителя, после чего в заместителе реализуются все функции, часто представляющие собой обычные заглушки. Хорошо спроектированная программа не заметит обмана, а плохо спроектированных программ мы же не пишем, верно? 😉

Это далеко не все "фундаментальные" шаблоны, но как пример они очень даже хороши, а если хочешь узнать побольше - гугль тебе в руки.

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

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

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

Прототип

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

Синглтон

А это - совсем грустный, одинокий прототип. Если в программе есть объект, который должен быть один на всех - тут-то этот шаблон и пригождается. Идея в том, что вместо создания объекта при помощи new мы создадим в нем метод и статичную (static) переменную - метод будет проверять, создан ли объект, если нет - то создавать и запоминать ссылку на него, а потом просто эту ссылку возвращать (кстати, это лишь вариант реализации - но, в любом случае, new вне класса использоваться не будет). Таким образом, мы можем использовать этот объект сходу, не особо волнуясь, создан ли он уже или нет, а вызывать нужно примерно так: OurSingletonClass::getInstance()->someMethod();.

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

Адаптер

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

Компоновщик

Помнишь еще про Шаблон функционального дизайна? Он связан с некоторой проблемой: если следовать ему, может случиться так, что ради нужной нам задачи придется создавать туеву хучу разных объектов. Чтобы не возиться со всем этим, придумали шаблон Компоновщик: он сам создаст эти объекты и скажет им что делать. Типа прораб или там прапорщик, кому что ближе. 😉

Третья из основных групп - "поведенческие" шаблоны. Они управляют алгоритмами внутри класса.

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

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

Стратегия

Пусть у нас есть несколько алгоритмов: один хорош, когда данных мало, а другой - когда много. А третий вообще замечателен, но работает лишь при определенных условиях. Если все это расписывать в цепочках if'ов, то никаких нервов не хватит потом это дорабатывать. А потому придуман шаблон Стратегия - в объект, созданный по этому шаблону, "запиханы" алгоритмы (оформленные в виде отдельных объектов), и алгоритм выбора алгоритма.

Состояние

Пусть мы пишем браузер, и у нас есть та самая картинка. Пока она не загружена - объект должен работать как прокси. Когда загрузилась - как обычная картинка. А если произошла ошибка - выводить иконку с красным крестиком. Такое изменчивое поведение описывается шаблоном Состояние (State). Обычно состояния оформлены в виде разных классов с единым интерфейсом.

Помимо шаблонов, входящих в эти группы, есть шаблоны, к ним не относящиеся. Например, очень распространенный шаблон MVC (Model View Controller, Модель Представление Контроллер). Идея в том, чтобы обработку данных, их получение и их вывод полностью изолировать друг от друга. Тогда, написав контроллер получения из модели и вывода списка пользователей с именем Василий, мы сможем, меняя класс Представления, выводить данные как в виде HTML, так и в виде XML, JSON, в виде документа Microsoft Word'a или картинки. А меняя класс Модели, мы сможем делать выборку на основе данных из БД, с других сайтов и т.д. Потрясающе, разве нет? И ведь код становится читабельнее, и фреймворков под MVC написано
немало.

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