Menu
Coddy logo textTech

Абстрактные методы в трейтах

Часть раздела Объектно-ориентированное программирование путешествия по PHP на Coddy. Урок 54 из 91.

Traits могут предоставлять готовые к использованию методы, но иногда вам нужно, чтобы class, использующий trait, реализовал определённую функциональность. В этом случае полезны абстрактные методы в traits — они определяют контракт, которому должен соответствовать использующий их class.

Когда trait объявляет абстрактный метод, любой класс, использующий этот trait, должен реализовать его:

<?php
trait Notifiable {
    abstract public function getEmail(): string;
    
    public function sendNotification(string $message): void {
        echo "Sending to " . $this->getEmail() . ": $message";
    }
}

class User {
    use Notifiable;
    
    public function __construct(private string $email) {}
    
    public function getEmail(): string {
        return $this->email;
    }
}

$user = new User("john@example.com");
$user->sendNotification("Welcome!");

Вывод:

Sending to john@example.com: Welcome!

Трейт предоставляет метод sendNotification(), но полагается на реализацию getEmail() классом. Это создаёт мощный шаблон, в котором трейт обрабатывает общую логику, делегируя специфичные для класса детали реализующему его классу.

Если class использует trait, не реализуя abstract method, PHP выдаёт фатальную ошибку. Это гарантирует, что traits могут безопасно зависеть от существования определённых методов, делая ваш код более предсказуемым и удобным в сопровождении.

challenge icon

Задание

Легко

Давайте создадим систему ценообразования, в которой разные типы продуктов по-разному рассчитывают свои итоговые цены, но используют общую логику скидок через trait с abstract методом.

Вы организуете код в трёх файлах:

  • Discountable.php: Create trait с названием Discountable, который предоставляет функциональность скидок. Trait должен содержать:
    • abstract method getBasePrice(): float, который должны Implement class, использующие этот trait
    • method applyDiscount(int $percent), который calculate и возвращает price после applying percentage скидки к базовой цене
    • method getFinalPrice(int $percent), который возвращает formatted строку: "Final price: $[discounted_price]" с 2 десятичными знаками
    Trait обрабатывает всю математику скидки, но полагается на каждый class, который сообщает ему, какова базовая цена.
  • Product.php: Create Product class, использующий trait Discountable. Подключите файл trait. Используйте продвижение constructor, чтобы Declare private $name (string) и private $price (float). Implement required getBasePrice() method, чтобы вернуть price продукта.
  • main.php: Подключите файл Product. Вы получите три inputs: название продукта, price и percentage скидки. Create экземпляр Product и Print две строки:
    • Базовая цена в formatted виде "Base: $[price]" с 2 десятичными знаками
    • result calling getFinalPrice() с percentage скидки

Этот pattern обладает большой силой, потому что trait Discountable может использоваться любым class — продуктами, сервисами, подписками, — если они Implement getBasePrice(). Trait предоставляет общую логику скидок, а каждый class определяет, откуда берётся его базовая цена.

Попробуйте сами

<?php

require_once 'Product.php';

// Считать входные данные
$name = trim(fgets(STDIN));
$price = floatval(trim(fgets(STDIN)));
$discountPercent = intval(trim(fgets(STDIN)));

// TODO: Создать экземпляр Product с именем и ценой

// TODO: Вывести базовую цену в формате "Base: $[price]" с 2 знаками после запятой

// TODO: Вывести результат вызова getFinalPrice() с процентом скидки

?>
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование

Потренируйтесь самостоятельно: Онлайн-компилятор PHP