← Назад к вопросам

Как используешь наследование?

2.3 Middle🔥 162 комментариев
#ООП

Комментарии (2)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Как я использую наследование в PHP Backend-разработке

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

Основные сценарии применения

1. Создание абстрактных базовых классов и шаблонных методов Я использую наследование для выделения общей логики в абстрактные классы, оставляя конкретную реализацию наследникам. Это реализация паттерна Template Method.

<?php

abstract class DataImporter
{
    // Шаблонный метод, определяющий скелет алгоритма
    final public function import(string $source): array
    {
        $this->validateSource($source);
        $rawData = $this->extractData($source);
        $processedData = $this->transform($rawData);
        $this->persist($processedData);
        
        return $processedData;
    }
    
    protected function validateSource(string $source): void
    {
        if (!file_exists($source)) {
            throw new InvalidArgumentException("Source not found: $source");
        }
    }
    
    abstract protected function extractData(string $source): array;
    abstract protected function transform(array $data): array;
    
    protected function persist(array $data): void
    {
        // Базовая реализация
        foreach ($data as $item) {
            $this->saveItem($item);
        }
    }
    
    abstract protected function saveItem(array $item): void;
}

class CsvImporter extends DataImporter
{
    protected function extractData(string $source): array
    {
        // Специфичная для CSV логика извлечения
        return array_map('str_getcsv', file($source));
    }
    
    protected function transform(array $data): array
    {
        // Трансформация CSV-данных
        return array_map(fn($row) => [
            'name' => $row[0],
            'value' => (int)$row[1]
        ], $data);
    }
    
    protected function saveItem(array $item): void
    {
        // Конкретная логика сохранения
        Database::insert('products', $item);
    }
}

2. Реализация полиморфизма через интерфейсы и абстрактные классы Наследование позволяет работать с разными типами через общий интерфейс базового класса:

<?php

interface NotificationSender
{
    public function send(string $message): bool;
}

abstract class BaseNotificationSender implements NotificationSender
{
    protected function log(string $message): void
    {
        Logger::info("Notification sent: $message");
    }
    
    abstract public function send(string $message): bool;
}

class EmailNotificationSender extends BaseNotificationSender
{
    public function send(string $message): bool
    {
        // Логика отправки email
        $result = Mailer::send($message);
        $this->log($message);
        return $result;
    }
}

class SmsNotificationSender extends BaseNotificationSender
{
    public function send(string $message): bool
    {
        // Логика отправки SMS
        $result = SmsGateway::send($message);
        $this->log($message);
        return $result;
    }
}

3. Расширение функциональности фреймворков и библиотек При работе с фреймворками вроде Laravel или Symfony, наследование часто необходимо:

<?php

namespace App\Http\Controllers;

use Illuminate\Routing\Controller as BaseController;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;

class Controller extends BaseController
{
    use AuthorizesRequests;
    
    protected function apiResponse($data, int $status = 200)
    {
        return response()->json([
            'success' => $status < 400,
            'data' => $data
        ], $status);
    }
}

class UserController extends Controller
{
    public function index()
    {
        $users = User::all();
        return $this->apiResponse($users);
    }
}

Критические принципы и ограничения

Я строго придерживаюсь следующих правил:

  • Глубина наследования не более 3-4 уровней — глубокие иерархии становятся хрупкими и сложными для понимания
  • Принцип подстановки Барбары Лисков (LSP) — наследник должен быть заменяем на родителя без изменения корректности программы
  • Избегание наследования для простого переиспользования кода — для этого лучше использовать композицию или трейты в PHP
  • Финальные классы по умолчанию — если класс не предназначен для наследования, помечаю его final

Альтернативы наследованию

В случаях, когда наследование неоправданно, я применяю:

  1. Композицию — включение объектов как свойств
  2. Трейты (traits) — для горизонтального переиспользования кода
  3. Делегирование — передача ответственности другим объектам
  4. Стратегию (Strategy pattern) — инкапсуляция семейства алгоритмов

Практический пример с трейтом

<?php

trait Timestampable
{
    protected \DateTime $createdAt;
    protected ?\DateTime $updatedAt = null;
    
    public function getCreatedAt(): \DateTime
    {
        return $this->createdAt;
    }
    
    public function setUpdatedAt(?\DateTime $date): void
    {
        $this->updatedAt = $date;
    }
}

class User
{
    use Timestampable;
    
    private string $name;
    
    public function __construct(string $name)
    {
        $this->name = $name;
        $this->createdAt = new \DateTime();
    }
}

class Product
{
    use Timestampable;
    
    private string $title;
    
    public function __construct(string $title)
    {
        $this->title = $title;
        $this->createdAt = new \DateTime();
    }
}

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