По мере накопления примеров «плохой практики» в программировании, эти примеры обобщаются в антипаттернах, образцах того, как не надо делать. Наиболее ярким антипаттерном в веб-программировании является ООП. Какой смысл говорить о мелких деталях, если сама соль перестала быть солёной?

ООП в PHP – антипаттерн

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

Для определённости пусть на сайте (в веб-приложении) будет 3 сущности: Пользователи, Статьи и Комментарии. Соответственно, будет 3 таблицы в БД: users, articles, messages. «Классическая» модель ООП традиционно строится «на кроликах»: для сущности «Кролик» создаётся класс «Кролик» с традиционными методами «Жрать», «С.ать» и «Убегать» (которые, возможно, «наследуются» от сущности Bestia). В нашем случае такая модель отображения реальности потребует как минимум трёх классов: Users, Messages, Articles (или «единичных» User, Message, Article, или даже всех шести разом), у которых будут всяческие попытки «напрямую» отобразить реальность – в виде методов типа «Зарегистрировать», «Опубликовать», «Удалить»...

Но веб-приложение гораздо более «плоское», чем природа. Например, методы Зарегистрировать (пользователя) и Опубликовать (статью, комментарий) для Приложения по сути являются одним действием – записью в таблицу БД. Вопрос: зачем здесь создавать разные методы и разные классы? Ведь можно одному и тому же методу Записать передавать в качестве параметра имя сущности (user, article).

Ещё раз: зачем нужны отдельные методы для добавления в БД нового пользователя и для добавления новой статьи? Посмотрите на эти методы, и увидите: при добавлении пользователя надо, например, шифровать пароль; а при добавлении статьи надо обрабатывать разрешённые HTML-тэги (и удалять запрещённые). Далее, пользователю, например, нужно отправлять письмо с результатами регистрации. То есть обрабатывать вводимую информацию нужно по-разному, потому и методы разные.

Но что будет, если логика изменится (например, HTML будет запрещён, или вместо md5 начнём использовать mysql password)? Нужно будет лезть в код, в методы обработки информации и менять код. Чтобы так не делать, лучше вынести все особенности полей БД в отдельную таблицу – описать их в Модели Метаданных. Тогда метод Записать будет просто сверять имя каждого поля с Метаданными и искать там особенности записи поля password из таблицы users или поля text из таблицы articles. Метаданные должны быть «звёздным» объектом, доступным из любого места Приложения (их не надо передавать в виде параметра).

В случае вынесения описания полей БД в отдельный массив Метаданных, и общий единственный метод Зарегистрировать, и общий метод Опубликовать будут работать одинаково правильно для всех таблиц. А если хорошо подумать, мы увидим, что в веб-приложении и методов-то в принципе возможно совсем мало (нет там никаких «жрать» или «с.ать»), это ведь не игра в Стратегию или в Перестрелку. То есть создание отдельных классов для каждой сущности оказывается совершенно неоправданным.

В хорошо спроектированном веб-приложении конкретные имена (сущностей, полей БД) должны встречаться в явном виде только в одном месте – в Модели (описании Метаданных). Контроллер разбирает Запрос пользователя в соответствии с договорённостью (логикой Приложения): например, считает сущностью SCRIPT_NAME, или определяет сущность по расширению (.htm, .php); по определённому Контроллером имени сущности (записанному в переменную) все части Приложения извлекают необходимую информацию из Метаданных – и Шаблонизатор, и Адаптер БД; и нигде больше имя сущности или имена полей в явном виде всплывать не должны (в идеале, даже в условных конструкциях конкретных методов).

Это в идеале. На самом деле, «дешевле» бывает всё-таки использовать конкретику. Например, при выводе списка (анонсов, сообщений...) надо сортировать данные сразу по трём полям. Можно, конечно, записать этот порядок сортировки в Метаданные, но это будет достаточно «жёсткое» закрепление; а если на другой странице Приложения нужен будет другой порядок сортировки, для избежания «поимённого» ORDER BY Метаданные придётся усложнять по экспоненте (или, на худой конец, квадратично).

Так мы скатываемся на уровень ООП и создаём методы, привязанные к конкретным сущностям. Когда спешим. Но принцип (или «паттерн»?) Метаданных продолжает действовать, и поспешно созданные методы постепенно исправляются. Например, можно создать отдельный раздел в Метаданных для Сортировки каждой сущности в разных ситуациях (не встроенный в основное описание полей, чтобы не раздувать объём). Это обычные моменты развития и рефакторинга: сначала, при добавлении нового функционала, в спешке неизбежно возникает ГК, потом он постепенно вычищается, код выравнивается. И рефакторить код, максимально отделённый от логики, на порядок проще, чем код, основанный на ООП (хотя там как бы и нечего рефакторить – ООП типа по определению само совершенство, написал – и любуйся потом всю жизнь).

Пример «хорошей практики» без ООП

Говорить можно много, но болтовня ничего не стоит. Как говаривал Линус, «покажите мне код!». Подойдёт и Ландау: «верховным судьёй всякой теории является опыт». Ядро теории в нашем случае – шаблон проектирования Данные – Метаданные – Контроллер – Представление. Почти MVC, но не совсем. Мы говорили об отличии понимания Модели в нашей теории от классического в статье php-фреймворк без ООП. Ну, вот сделанный на коленке пример более точного фреймворка sansoop: http://ir2.ru/static/sansoop/, основанного на тезисах этой статьи. Исходный код: http://ir2.ru/static/sansoop/sansoop.zip (6 К).

D.M., admin
Курсы Автокад, м. Парк Культуры - архитектурное проектирование обучение. Как быстро освоить компьюте
Автоцистерна, наливная перевозка - транспортировка молока, масел и горючих веществ!

Комментарии

Дилетант 24.04.2015 14:06:50

Но ведь работает! Этот сайт – работает. Другие – работают... ахинея в действии лучше, чем "правильный" код на бумаге.

  01.04.2015 03:10:03

Сударь, вы поехавший и несёте полную ахинею.