(Вообще, хз, зачем я это написал. На работе приняты другие правила, а с моим сайтом никто, кроме меня, не работает. Но коль написал, пусть будет.)
Большая часть времени жизни программного продукта отдаётся не разработке, а поддержке его кода. Плохо структурированный и оформленный код заставляет программистов раз за разом встречаться с проблемой понимания, зачем что-то нужно, как с этим работать, где найти нужные модули, каковы логические связи между разными компонентами кода.
Человечество не впервые встречается с такой проблемой. Любую программу можно написать на языке машинных команд, но люди придумали вместо того процедуры. Процедурно можно описать любую структуру организации, но это оказывается неэффективным, когда речь идёт о крупных системах; и люди придумывают ООП. Не совсем верно считать, что программа разработана в согласии с парадигмой ООП, если она просто разбита на классы.
Вообще в php, молодом языке, поддержка ООП появилась и вовсе недавно, и идеологически она почти целиком скопирована с языка Java, где принципы и шаблоны программирования, дающие наиболее эффективный результат, уже давно обкатаны многими поколениями программистов. Эти принципы оказываются оптимальными и в условиях новой программной среды.
Для разработки более качественного объектного php-кода разумно придерживаться следующих правил.
1. Для имитации вложенных пространств имён – вертикальных связей в дереве классов – используется символ «_». Это позволяет естественным образом разделять корневые и связанные классы визуально.
Например, элемент каталога может существовать сам по себе, тогда как рубрикатор каталога – организующая настройка, невозможная без элементов. Статистика просмотров элемента – подчинённый класс, кэш статистики – двойное подчинение.
2. Имя класса однозначно соответствует имени файла, в котором он хранится. В Java общепринятое правило – замена “_” на “/” в именах классов. В каждом файле обязательно должен быть соответствующий публичный класс.
3. Имена интерфейсов имеют префикс i_. Это позволяет всегда понимать, какой аргумент стоит справа у оператора instanceof и не путать интерфейсы с классами. Так как php, вслед за Java, не допускает множественного наследования, без использования интерфейсов невозможна полноценная реализация полиморфизма.
4. Не нужно следить за подключаемым кодом. Благодаря однозначному соответствию «файл»-«класса», эту функцию можно полностью отдать __autoload().
5. Не нужно использовать виртуальные методы для доступа к атрибутам объекта. Вместо этого существуют функции __get и __set, использование которых более прозрачно и естественно. Программе становится безразлично, с чем она работает. При вёрстке не нужно помнить, какой префикс и стиль используется в __call в данном случае. Имена полей в базе данных могут иметь любой стиль именования, как с использованием «_», так и в Camel Style.
6. Избегайте создания «божественных объектов». Все классы и методы должны быть лаконичными. Класс должен отвечать только той задаче, ради которой он создан. Весь связанный код, который когда-либо может быть использован повторно, должен быть инкапсулирован в другие соответствующие его функциональности классы.
7. Все функции типа public или protected обязательно должны иметь документацию, соответствующую общепринятым стандартам. Комментарий выглядит примерно так:
/**
* This function returns formatted date by passed timestamp or current time
* <code>
* echo env::timeToDate();
* </code>
*
* @param int $timestamp
* @return string
*/
static public function timeToDate($timestamp=null)
- Первый блок комментария содержит краткое описание его функциональности. Если использование документируемого метода может быть нетривиальным, используются теги <code>
- Параметры описываются по шаблону: @param тип|другойтип $имяпараметра комментарий. $имяпараметра должно давать однозначное представление о его использовании, иначе в комментарии должна иметься соответствующая сноска.
- Возвращаемое значение, если оно не void, описывается как @return тип.
- Если метод может генерировать исключение, пишется @throws тип_исключения.
Это позволит не только ускорить понимание, как что работает, но и, если используется качественная IDE, поможет с type hinting и позволит генерировать без затрат труда документацию в WDSL или HTML. В случае, если такая потребность подразумевается, следует также использовать директиву @package и писать краткую документацию для каждого класса. Авторы указываются в директиве @author по одному на строке.
Не нужно держать в памяти всю информацию о проекте. Это позволит освободить разум для более высоких задач.
8. Используйте стандарты именования не только классов, но и методов, чтобы упростить понимание их функционального назначения и вида возвращаемых значений.
Например, для поиска конкретного объекта по какой-то связке, если метод возвращает объект или null, используйте префикс get, как-то:
static public function getById($id)
Если метод возвращает null или список из неизвестного количества объектов, используйте префикс search, как-то:
static public function searchByCityName($city)
В именах методов часто логичным бывает использование Camel Style: каждое следующее слово начинается с заглавной буквы.
9. Методы, не требующие наличия объекта, должны быть статическими.
10. Поведение функции должно соответствовать её изначальному предназначению даже в том случае, если вам хочется её расширить для решения более широкого класса задач. Если расширение не нарушает логику поведения функций, оно должно быть задокументировано. Иначе оно должно быть реализовано отдельно.
11. Желательно инкапсулировать поперечные связи типа «один ко многим» или «многие ко многим» в зависимых объектах. Например, вместо того, чтобы писать метод ::searchByCategoryId($id) для элементов каталога, лучше использовать метод ->searchListings() в модели категории, использующий публичные методы элементов.
Это позволит легко изменять организацию представления независимых элементов в согласии с тем, какие задачи стоят перед проектом в конкретном случае. Например, добавлять и убирать рубрикаторы, не касаясь корневого класса; изменять методы хранения связей (по полю в одной из таблиц, или по специальной таблице связей, или другим способом) и производить другой рефакторинг кода с минимальными затратами труда.
12. В каждой директории проекта должно располагаться небольшое количество файлов и других папок. Если файлов больше десятка, скорее всего, они являются развёрнутой иерархической структурой.
13. Не следует строить значительные компоненты проекта на чужом коде. С большой вероятностью, чужой код поведёт себя неожиданно тогда, когда без него обойтись будет уже невозможно, но не ранее. Поддержка такого кода многократно усложняется.
14. Если переменная может быть использована внутри метода позже или если она инициирована в другом фрагменте кода, она должна быть документирована согласно принятым стандартам. Например:
/* @var $listing ListingModel */
Это позволит всегда знать, какие переменные используются в данной области видимости. Кроме того, качественные IDE будут подсказывать программисту, как работать с такими переменными.
15. Подразумевайте возможность расширения и многократного использования кода. Если просто держать в голове мысль о том, что написанный код понадобится позже вам и другим программистам для решения подобных или новых задач, код становится более прозрачным.
Хороший код не просто хорошо работает, но и с ним работать – хорошо. Подразумевать масштабируемость необходимо всегда. Никакая программная система не пишется на века, никогда она сходу не достигает поставленных высот в уровне организации. Крупная программа может только выращиваться и последовательно дополняться.