Приветствую, коллеги.
Вместо вступления
Написание этой статьи вызвано большим интересом к теме программирования "как оно есть", имея ввиду именно создание программы, отвлеченно от программирования на PHP или других языках. Что же здесь имеется ввиду? То, что программирование - это вполне теоретизируемая область, обладающая рядом постулатов, правил, методов. И именно незнание этой теории, на мой взгляд, приводит к тому, что называется "быдлокодом" (Лично мне не нравится это название, поэтому здесь и в дальнейшем буду называть это просто "нестройный код")
Кратко о теории
Пересказать теорию я, наверное, не сумею, да и заняло бы это слишком много. Теория ведь бывает разная. И я, к примеру, изучал ее в течении нескольких лет в ВУЗе. Курсы, которые преподавались там - это Структуры Данных и Алгоритмы (СДА), Языки Программирования и Методы Трансляции (ЯПМТ), Компьютерная Графика (КГ), Интернет-Технологии (ИТ) и т.п. Как видите, пересказать все это довольно трудоемко. Поэтому расскажу лишь немного о самой теории, но не ее содержание.
Теория позволяет воспринимать процесс абстрактно, посредством сущностей и связей между ними, а так же побуждает к осознанию сущностей, которые Вы используете в конкретной программе. Приведу пример. Простой код, задача которого - сделать замену в тексте так, чтобы все первые буквы были заглавными. Вариант первый:
1. EuGen - 01 Декабря, 2011 - 10:48:32 - перейти к сообщению
Вариант второй:
Примеры приведены с тем, чтобы показать наличие сущностей. В первом примере их целых три. Это $t, $i и $r. Да-да, переменные - один из видов сущностей. Ведь создавая переменную, мы закладываем в неё некоторый смысл. И пусть $i используется только в цикле, а $t - временная. Обе имеют смысл, первая - как итератор цикла, вторая - как временное хранилище. $r в первом случае есть сущность-результат.
Теперь придем к понимаю, почему первый пример хуже второго. Хуже - с точки зрения теории программирования. Дело здесь не в том, что второй выглядит "красивее", имея всего лишь одну строчку кода. Если знать, почему так произошло, это будет лишь естественным следствием. А дело в простом. В первом примере у нас есть две лишних сущности. Это $t и $r. Почему лишних? Потому что, очевидно, ни итератор цикла, ни временное хранилище не имеют дальнейшего значения, их роль ограничена лишь этим участком. И, конечно, кроме практической цели у них нет строгого логического обоснования. Попросту говоря, они - просто переменные. Сущностью в первом примере является только $r - так как это - результат (с которым, конечно, предполагается что-то делать). И, скорее всего, данные пришли исходя из определенного алгоритма, а потому $r будет участвовать далее как сущность. Во втором же примере мы обходимся без лишних сущностей (есть только одна - собственно, результат), а потому эти два примера иллюстрируют одно из правил:
0. Избегать лишних сущностей в программе
Помимо сущностей есть еще и связи между ними. А так же более сложные сущности, чем, скажем, переменная. Здесь, конечно же, идет отсылка к ООП. Имею ввиду именно сам объектный подход - не его реализацию в конкретном языке, а именно теорию. Каждый раз я вижу, что, пытаясь постигнуть ООП, начинающие программисты безнадежно увязают в хитросплетениях его конкретной реализации в конкретном языке. А это - не есть ООП, так как ООП - это парадигма, теоретическое построение, если хотите. Поэтому нельзя вникнуть в него просто начав "писать код". Осознание должно быть на теоретическом уровне, абстрактном уровне мышления. Ровно поэтому при описании, обучению ООП часто можно видеть отвлеченные от программирования примеры ("Представим себе, что у нас есть сущность - фрукт, и ее экземпляры - банан, апельсин, груша, и у них есть свойства {...} и т.п."). Ведь если все мы вспомним, как в детстве нас учили числам - мы придем к тому же самому. Все помним, как считали яблоки в раннем детстве? Это может показаться смешным или странным, но именно тогда в нас всех закладывалось абстрактное восприятие чисел. Спросите сейчас себя - что такое "число семнадцать"? Вы не найдете точного ответа, так как это - абстракция, отвлеченно от реального применения (семнадцать яблок, семнадцать мгновений весны и т.п.) не имеющая конкретного смысла. Аналогично и ООП в программировании. Это - абстракция, это - теория. И разница в понимании/непонимании ее - на том же уровне, как понимание/непонимание абстракции чисел. Оттого и сложно ее постичь.
Как помочь в восприятии
Если рассматривать код с теоретической точки зрения, то в хорошем коде сущности обозначают сами себя и множество допустимых связей и отношений с другими сущностями. Можно помочь сделать это обозначение в самом коде. Как правило, неймспейсинг и правила написания призваны это сделать. Иными словами, сейчас мы снова пришли к правилу, которое является следствием теоретического подхода:
1. В хорошем коде сущности выделяют себя сами. И этому помогает выделение в самом коде
- это именно правила "хорошего тона" в программировании, при создании имен сущностей. Хорошее имя переменной скажет вам, зачем она нужна, а хорошее имя функции - что она делает. Как видите, правила хорошего кода тоже возникли не на ровном месте и не только для удобства, а являются следствием теоретического обоснования. Чтобы была конкретика, приведу правила, которых придерживаюсь сам.
0. Неймспейсинг:
- Имена переменных. Используются префиксы, указывающие на тип переменной, обозначая таким образом множество допустимых над ней операций. Я использую префиксы:
rg - для массивов
b - для булевых данных
i - для данных целочисленного типа
f - для данных вещественного типа
s - для данных строкового типа
r - для данных типа ресурс или объект.
m - для данных, тип которых заранее неизвестен (часто при лямбда-функциях, используемых при обработке массивов)
fn - для callback-данных
- Имена функций.
Используются как префиксы, так и словообразование. Например, функцию, возвращающую MAC-адрес по IP, я назову getMACbyIP. Традиционно функции я именую с маленькой буквы, помещая действие, которое выполняет функция, в начало ее названия и отделяя его строчным написанием.
- Имена классов. Всегда с заглавной буквы.
Класс ассимилирует в себе свою принадлежность. Следствием этого является простота написания маршрутизации, используемой при автозагрузке классов (через __autoload)
- Имена методов.
- все публичные методы объявляются с самого начала и следуют названиям функций.
- все защищенные (protected) методы начинаются с
подчеркивания, следуя названиям функций
- все приватные (private) методы начинаются с
двойного подчеркивания, следуя названиям функций
1. Объявление сущностей.
Здесь имеется ввиду, что все переменные инициализируются, прежде чем будут использованы.
2. Избегать приемов, затрудняющих чтение имени.
Для PHP это, прежде всего, неочевидный динамический вызов. Нельзя сказать, что он - плох, и в ряде случаев его использование позволяет решить задачу красиво и лаконично, но злоупотреблять переменными-функциями и переменными-методами не стоит.
Кажому - свое
Помимо того, что сущности должны быть четко определены, иметь обозначенный (или обозримый) круг связей и отношений с другими сущностями, они должны еще и нести именно тот набор ролей/функций, которые отвечают их логическому обоснованию.
Иными словами, каждый метод класса должен решать именно ту задачу, для которой создавался, и никак не заботиться о чем-либо еще, никак не брать на себя какие-либо другие функции. Снова рассмотрим пример. Допустим, у нас есть функция, добавляющая пользователя в БД. У пользователя есть логин и пароль. Первый пример:
PHP:
скопировать код в буфер обмена
скопировать код в буфер обмена
- function newuser_add($login, $password, $link=null)
- {
- if(!$login || !$password)
- {
- return false;
- }
- if(!mysql_query('INSERT INTO users (login, password) VALUES ("'.mysql_real_escape_string($login)."', '".mysql_real_escape_string($password).'")', $link))
- {
- }
- }