popov . dev

Main

Library

Articles

Осваиваем принци...

Осваиваем принципы SOLID в Laravel

Принципы SOLID представляют собой набор принципов проектирования, которые могут превратить ваш код на Laravel в шедевр (или, по крайней мере, не дать ему выглядеть как спагетти). Сейчас мы познакомимся с принципами SOLID и узнаем, как реализовать их в Laravel и узнаем их основные преимущества и способы реализации.

Что такое принципы SOLID?

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

Значение аббревиатуры SOLID:

  • Принцип единой ответственности (SRP)
  • Принцип открытия/закрытия (OCP)
  • Принцип замещения Лискова (LSP)
  • Принцип разделения интерфейсов (ISP)
  • Принцип инверсии зависимостей (DIP)

Принцип единой ответственности (SRP)

Один класс, одно действие. Точка.

Представьте, что ваш руководитель дает вам список дел из 50 пунктов. Это ошеломляет, не так ли? Классы чувствуют то же самое, когда вы перекладываете на них множество обязанностей. Принцип единой ответственности гласит, что у класса должна быть только одна причина для изменений.

Пример. Контроллер - это не место для хранения бизнес-логики, запросов к базе данных или секретных рецептов печенья. Давайте наведем порядок:

Как делать не надо:

class OrderController extends Controller
{
    public function store(Request $request)
    {
        $order = new Order();
        $order->user_id = $request->user()->id;
        $order->total = $this->calculateTotal($request->items);
        $order->save();

        $this->sendOrderEmail($order);

        return response()->json(['success' => true]);
    }

    private function calculateTotal($items)
    {
        // Вычисляем сумму...
    }

    private function sendOrderEmail($order)
    {
        // Отправляем почту...
    }
}

Как следует сделать:

Разделите это на отдельные обязанности:

class OrderController extends Controller
{
    public function store(Request $request, OrderService $orderService)
    {
        $orderService->createOrder($request);

        return response()->json(['success' => true]);
    }
}
class OrderService
{
    public function createOrder($request)
    {
        $order = new Order();
        $order->user_id = $request->user()->id;
        $order->total = $this->calculateTotal($request->items);
        $order->save();

        $this->sendOrderEmail($order);
    }

    private function calculateTotal($items)
    {
        // Вычисление суммы...
    }

    private function sendOrderEmail($order)
    {
        // Отправка почты...
    }
}

Почему? Если логика отправки электронной почты изменится, вы будете использовать OrderService, а не OrderController. Все логично и максимально понятно.

Принцип открытия/закрытия (OCP)

Открыто для расширения, закрыто для модификации

Представьте себе OCP как табличку "не прикасаться" в музее: расширяйте функциональность, не нарушая и не модифицируя существующий код.

Пример. Допустим, мы рассчитываем скидки.

Как делать не надо:

class DiscountCalculator
{
    public function calculate($order)
    {
        if ($order->user->isVip()) {
            return $order->total * 0.2; // VIP скидка
        }

        if ($order->total > 100) {
            return $order->total * 0.1; // Массовая скидка
        }

        return 0;
    }
}

Что произойдет, если завтра вы введете скидку "Черная пятница"? Очевидно будет хаос изменений!

Как следует сделать:

Используйте полиморфизм:

interface Discount
{
    public function calculate($order);
}

class VipDiscount implements Discount
{
    public function calculate($order)
    {
        return $order->user->isVip() ? $order->total * 0.2 : 0;
    }
}

class BulkDiscount implements Discount
{
    public function calculate($order)
    {
        return $order->total > 100 ? $order->total * 0.1 : 0;
    }
}

Теперь вы можете расширить это, не изменяя исходные классы. Добавьте BlackFridayDiscount или даже PizzaLoverDiscount!

Принцип замещения Лискова (LSP)

Производные классы должны быть взаимозаменяемы со своими базовыми классами

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

Пример. Давайте возьмем, к примеру, птиц:

Как делать не надо:

class Bird
{
    public function fly()
    {
        return "Я лечу!";
    }
}

class Penguin extends Bird
{
    public function fly()
    {
        throw new Exception("Пингвины не летают!");
    }
}

Летающий пингвин? Похоже на нового персонажа Marvel.

Как следует сделать. Соответствующим образом разделяйте модели поведения:

interface Bird
{
    public function move();
}

class Sparrow implements Bird
{
    public function move()
    {
        return "Я лечу!";
    }
}

class Penguin implements Bird
{
    public function move()
    {
        return "Я плаваю!";
    }
}

Теперь ваш код не выйдет из строя в зоопарке.

Принцип разделения интерфейсов (ISP)

Не заставляйте классы внедрять то, что они не используют

Представьте, что вы заказываете пиццу, но вынуждены изучать все меню целиком. При перегрузке интерфейсы работают точно так же.

Рассмотрим пример, как делать не надо:

interface Payment
{
    public function payWithCreditCard();
    public function payWithPaypal();
    public function payWithBitcoin();
}

class CreditCardPayment implements Payment
{
    public function payWithCreditCard()
    {
        // Процесс оплаты кредитной картой
    }

    public function payWithPaypal()
    {
        throw new Exception("Не реализовано.");
    }

    public function payWithBitcoin()
    {
        throw new Exception("Не реализовано.");
    }
}

Платежный интерфейс, который работает только для одного способа оплаты? Это все равно что взять ложку в драку на ножах.

Как следует сделать:

Разделите интерфейс на более мелкие, конкретные контракты:

interface CreditCardPayment
{
    public function payWithCreditCard();
}

interface PaypalPayment
{
    public function payWithPaypal();
}

interface BitcoinPayment
{
    public function payWithBitcoin();
}

Теперь вы реализуете только то, что вам нужно.

Принцип инверсии зависимостей (DIP)

Полагайтесь на абстракции, а не на конкретные вещи

Представьте, что ваш кофе варит бариста, и это не зависит от конкретной марки зерен. Принцип способствует гибкости, опираясь на абстракции.

Рассмотрим пример, как делать не надо:

class Notification
{
    public function sendEmail($message)
    {
        // Логика отправки почты...
    }
}

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

interface Notifier
{
    public function send($message);
}

class EmailNotifier implements Notifier
{
    public function send($message)
    {
        // Отправка почты
    }
}

class SMSNotifier implements Notifier
{
    public function send($message)
    {
        // Отправка SMS
    }
}

class NotificationService
{
    private $notifier;

    public function __construct(Notifier $notifier)
    {
        $this->notifier = $notifier;
    }

    public function notify($message)
    {
        $this->notifier->send($message);
    }
}

Теперь заменить EmailNotifier на SMSNotifier так же просто, как сменить марку кофе.

Приготовьте это со вкусом Laravel

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

  • SRP = Меньше заданий на класс = более качественный код.
  • OCP = Добавлять, а не редактировать.
  • LSP = Соблюдайте правила замещения.
  • ISP = Меньший размер интерфейсов = Более простая реализация.
  • DIP = Абстрактные зависимости = Гибкие конструкции.

Собственно, внедряйте эти принципы в свои проекты на Laravel. В будущем вы (и ваша команда) будете благодарны вам. И обязательно помните: делайте все ОСНОВАТЕЛЬНО, а не спагетти код!

Подведем итоги

Применение принципов SOLID в ваших проектах на Laravel — это не просто написание более чистого кода, это создание систем, которые проще поддерживать, расширять и которые вам нравятся. Задавая каждому классу четкую цель, сохраняя гибкость кода и полагаясь на абстракции, вы настраиваете себя на более приятный процесс разработки (и сокращаете количество ночных сеансов отладки).

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

В следующий раз, когда ваш код начнет напоминать спагетти, вспомните об этих принципах и спросите себя: "Правильно ли я его составляю?"

Comments

In order to leave your opinion, you need to register on the website