Почему классы лучше чем функции при разработке масштабных продуктов на PHP
Дисклэймер: мне нужно было объяснить моему сотруднику зачем нужны классы в принципе, а именно в чём их преимущество перед функциями. Я начал фантазировать примеры кода, и написал много комментариев. В итоге я посмотрел на то что получилось и подумал - а не дерзнуть ли в песочницу на Хабре?
Что такое хороший код?
Это востребованный код, то есть код который работает в живом проекте
Это не сильно связанно с темой, но это нужно понимать, так как остальное следует из первого пунктаЭто код, который работает с минимальным количеством ошибок
Это код, который удобно поддерживать и расширять (не важно, будет ли это делать один человек или команда разработчиков)
Классы помогают писать хороший код
Буду рассматривать классы не как необходимый инструмент парадигмы ООП, а как инструмент проектирования системы (и стараться доказать их преимущества перед функциями)
Читаемость
<?php
$Article = new Article($articleId);
$Article->setMeta('');
Банально читается понятнее чем
<?php
article_set_meta($articleId, '');
Даже без подсветки синтаксиса IDE. А с подсветкой - кратно понятнее.
Да, это одна лишняя строчка кода, но если речь о количестве символов - в масштабах проекта это экономия.
Пример более очевиден с увеличением масштаба
<?php
$Article = new Article($articleId);
$Article->setMeta('')->addImage('')->markAsHot();
$Article->render();
VS
<?php
article_set_meta($articleId, '');
article_add_image($articleId, '');
article_mark_as_hot($articleId);
article_render($articleId);
Организация кода
Структура с нэймспэйсами и классами визуально понятнее
<?php
/Article/Article::create();
/Article/Gallery/Gallery::create();
/* VS */
article_create();
article_gallery_create();
При соблюдении хоть каких-то стандартов код с классами сразу рисует в голове структуру проекта, просто мозг так работает.
Да, любая IDE по клику на метод или функцию найдёт её, но в случае с функциями мозгу не очевидна структура, особенно в большом проекте где 10-ки классов и методов (или сотни функций)
В командной разработке очень удобно отдавать разные Классы/Нэймспэйсы, можно сказать, модули, разным исполнителям.
Ассоциация с реальными вещами в жизни
Когда мы мыслим в рамках классов - нам проще строить сложные структуры, потому что мозг ассоциирует классы и методы с реальными (или абстрактными) понятиями
<?php
$User = new User(); //Посетитель, человек
$Article = new Article($id); // Статья на сайте, читаемый на мониторе текст
$Article->readBy($User); // Статья прочитана человеком
$user = get_from_session(); // Что-то про сессии
$article = get_article_by_id($id); // Что-то get и id
article_read_by_user($article, $user); // Вроде понятно, но не с первого прочтения
Инкапсуляция, как инструмент скрывания внутреннего функционала
Private и Protected методы нужны не для абстрактных доктрин ООП.
Самое банальное - при подстановке кода IDE не будет показывать Private и Protected методы
Опять же, про работу мозга - такие методы помогают переключаться с объекта и его внутренних методов на более высокие уровни структуры, когда мы работаем с конкретным объектом - мы можем быстро сфокусироваться на нём, когда на высоком уровне - "забыть" какие-то внутренние методы объекта. С функциями такое просто не получится, потому что они все видны, мозгу сложно держать в памяти их все, также как сложно вспоминать конкретные наборы функций, потому что он перебирает все.
А самое главное - без таких методов не получится хорошо спроектировать систему, немного об этом ниже
Шаблоны проектирования
Шаблоны проектирования - набор отточенных годами практик, которые помогают решать сложные задачи, причём таким образом, чтобы было понятно всем (кто знаком с реализованным шаблоном). Без классов(и ООП) они просто не получатся. Конечно, можно сделать их на функциях... Хотя нет, нельзя.
И, немного о архитектуре
Хороший проект - это не просто код, это код, связанный архитектурой. Архитектура задаёт стандарты, помогает разным программистам работать над разными частями, повышает отказоустойчивость системы. Во многом это делается благодаря уровням.
Самый высокий уровень
<?php
$Client = new Client($clientId);
$Product = new Product($productId);
$Invoice = InvoiceBuilder::create($Client);
$Invoice->addProduct($Product, $amount, $sellPrice);
$Invoice->checkout();
На этом уровне мы не обязаны знать что там под капотом. Мы пишем понятную человеческими словами логику. Нам очевидно что мы выписали счёт клиенту. По этому и классы: чисто визуальнее они понятнее чем функции, вот есть счёт, мы что-то в него добавили, и выписали его.
Средний уровень
<?php
class Invoice
{
public function addProduct(Product $Product, int $amount, int $sellPrice)
{
// Добавим товар в счёт
$this->products[] = [
'product' => $Product->id,
'amount' => $amount,
'sellPrice' => $sellPrice,
];
// Запомним выбор клиента, если он не оформит покупку - будем ему спамить этот товар
$EvilTracker = new EvilTracker($this->Client);
$EvilTracker->track($Product->id);
// Создадим запись для отдела аналитики по добавленному товару
ProductAnalytics::log($Product->id, $amount, $sellPrice, 'added-to-basket');
}
}
Смысл его в том что на этом уровне мы можем менять поведение системы, не затрагивая верхний уровень, добавить какие-то действия, убрать, и тд. Здесь классы нужны чтобы
Реализовывать инкапсуляцию, делать код "модульным" и прятать методы-функции от других классов-модулей синтаксис классов и нэймспэйсов для этого просто удобен.
Реализовывать наследование, это очень хорошая штука для разработки и проектирования.
Реализовывать шаблоны проектирования.
При хорошей архитектуре мы меняем EvilTracker() на SpareTracker() и всё работает.
Низкий уровень
<?php
class
{
public function someLowLevelLogic(bool $boolSomething):array
{
$someArray = mysql::get_something();
if ($boolSomething) {
$someArray = array_map(function ($element) {
return [
'id' => $element['id'],
'title' => $element['title'] . ' ' . $element['bankTitle'],
];
}, $someArray);
}
returm $someArray;
}
}
Разделение на уровни и модули (под модулями я имею ввиду /Разные/Нэймспэйсы и наборы классов) помогает делать проекты, которые удобно развивать как в одиночку, так и командами. Лишние пару строк на старте разработки нового функционала помогает экономить часы доработки.
P.S. Получилось немного сумбурно, я пытался от простого к сложному обосновать пользу понимания и использования классов, даже без каких-то лютых наследований и трейтов.
Прошу судить строго, так как это помогает двигаться вперёд.
UPD: я был не прав в том что не указал контекст.
Контекст - "при разработке масштабных продуктов на PHP"