Базовые определения ООП

  1. Класс
  2. Пространство имён
  3. Объект
  4. Конструктор
  5. Деструктор
  6. Клонирование
  7. Наследование (базовый класс)
  8. Интерфейс
  9. Абстрактный класс
  10. Перегрузка функций
  11. Финализация
  12. Область видимости свойств и методов
  13. Статичные методы и переменные
  14. Позднее связывание
  15. Шаблоны проектирования (паттерны)
  16. Принципы SOLID

Класс

Ключевое слово: class

Документация: https://www.php.net/manual/ru/language.oop5.basic.php

Описание:

С помощью классов мы описываем модели, разделяем сложную логику на маленькие кусочки. Чем проще и понятней написан класс, тем удобнее будет с ним работать. И наоборот, чем больше у класса свойств и методов, тем бесполезней он для программиста (см. золотой молоток).

Пример использования:

class MyClass
{
    // Свойства
    public $var1, $var2, $var3;
    
    // Методы
    public function calc($arg1, $arg2) {
        // Логика поведения
        return $this->var1 * $arg1 + $arg2;
    }
}

$object = new MyClass();  // Из класса создаются объекты
$object->var1 = 10;

echo $object->calc(20, 30);  // 230

[Наверх]


Пространство имён

Ключевые слова: namespace

Документация: https://www.php.net/manual/ru/language.namespaces.rationale.php

Описание:

Пространства имён нужны для удобства управления большим количеством классов. С помощью них мы работаем с классами как с файлами и папками. Ещё они помогают разрешить конфликты имён: если два класса имеют одно название, просто задайте им разные namespace.

Пример использования:

Файл: classes/MyShop/Order.php

<?php
namespace MyShop;

// Класс заказа
class Order {
    // Возвращает строчки заказа
    function lines() {
        return [
            new Order\Line(),  // Строчка 1
            new Order\Line(),  // Строчка 2
        ];
    }
}
?>


Файл: classes/MyShop/Order/Line.php

<?php
namespace MyShop\Order;

// Строчка заказа
class Line {
    // ...
}
?>


Файл: index.php

<?php

$order = new MyShop\Order();
var_dump($order->lines());

[Наверх]


Объект

Ключевое слово: new

Документация: https://www.php.net/manual/ru/language.oop5.basic.php#language.oop5.basic.new

Описание:

У объектов мы вызываем методы и задаём свойства. Каждый объект является экземпляром какого-то класса. Объекты всегда передаются по ссылке (это означает, что изменения объекта будут доступны везде).

Пример использования:

$obj1 = new MyClass($arg1, $arg2);  // Создание объекта из класса MyClass с параметрами
$obj2 = new OtherClass;             // Создание объекта из класса OtherClass без параметров

[Наверх]


Конструктор

Ключевое слово: __construct

Документация: https://www.php.net/manual/ru/language.oop5.decon.php#language.oop5.decon.constructor

Описание:

Функция-конструктор однократно вызывается для каждого объекта при создании. В ней мы задаём начальное состояние нового объекта. Например, можно получить данные через аргументы и записать их в контекст объекта. Также можно создавать зависимые объекты, которые нужны для работы. Важно помнить, что функция-конструктор не должна возвращать значение.

Пример использования:

class MyClass {
    private $arg1, $arg2;
    
    // Объявляем функцию-конструктор
    function __construct($arg1, $arg2 = 123) {
        $this->arg1 = $arg1;
        $this->arg2 = $arg2;
    }
    
    function calc() {
        return $this->arg1 + $this->arg2;
    }
}

$sum = new MyClass(10, 20);
echo $sum->calc(); // 30

[Наверх]


Деструктор

Ключевое слово: __destruct

Документация: https://www.php.net/manual/ru/language.oop5.decon.php#language.oop5.decon.destructor

Описание:

Функция-деструктор вызывается однократно для каждого объекта при уничтожении. Мы используем её для очистки ресурсов объекта. Например, сделали класс для работы с изображениями и нужно, чтобы ресурсы очищались сразу, как только умрёт объект. Ещё деструкторы удобно использовать при отладке, чтобы понимать, как долго живёт объект.

Пример использования:

class MyClass {
    // Объявляем деструктор
    function __destruct() {
        printf("Объект %s(#%s) уничтожен\n", 
            get_class($this),
            spl_object_id($this)
        );
    }
}

$obj1 = new MyClass();
$obj2 = new MyClass();

unset($obj2);  // Объект MyClass(#2) уничтожен
unset($obj1);  // Объект MyClass(#1) уничтожен

[Наверх]


Клонирование

Ключевое слово: clone

Документация: https://www.php.net/manual/ru/language.oop5.cloning.php

Описание:

Мы используем клонирование, чтобы создавать дубли объектов. Это единственный способ создать копию, поскольку объекты всегда передаются по ссылке.

Пример использования:

// Создаём объект
$a = new stdClass();
$a->foo = 'bar';

// Простое присваивание передаёт ссылку на начальный объект
$b = $a;
$b->foo = '123';

// Мы изменили $b, но данные поменялись и в $a
echo $a->foo, PHP_EOL;  // 123

echo '---', PHP_EOL;

// Клонируем объект
$c = clone $b;
$c->foo = 'bee';

// Мы изменили $c, данные поменялись только в $c
echo $a->foo, PHP_EOL;  // 123
echo $b->foo, PHP_EOL;  // 123
echo $c->foo, PHP_EOL;  // bee

[Наверх]


Наследование (базовый класс)

Ключевое слово: extends

Документация: https://www.php.net/manual/ru/language.oop5.inheritance.php

Описание:

В базовый класс мы выносим общие свойства и методы, которые могут использовать потомки. Таким образом выстраивается иерархия классов. Нужно помнить, что в PHP базовый класс может быть только один.

Пример использования:

// Базовый класс
class Decorator {
    private $text;
    
    function __construct($text) {
        $this->text = $text;
    }
    
    // "Магическая функция", которая вызывается для преобразования объекта в строк
    function __toString() {
        return $this->text;
    }
}

// Класс-потомок
class Bold extends Decorator {
    function __toString() {
        return "<strong>" . parent::__toString() . "</strong>";
    }    
}

// Класс-потомок
class Italic extends Decorator {
    function __toString() {
        return "<i>" . parent::__toString() . "</i>";
    }    
}

echo new Bold("Жирный"), PHP_EOL;       // <strong>Жирный</strong>
echo new Italic("Наклонный"), PHP_EOL;  // <i>Наклонный</i>

[Наверх]


Интерфейс

Ключевые слова: interface, implements

Документация: https://www.php.net/manual/ru/language.oop5.interfaces.php

Описание:

Мы используем интерфейсы, когда хотим указать, какие функции нам нужны. Это удобно, когда классов ещё нет или они только проектируются. Также с помощью интерфейсов можно отвязаться от конкретных классов, если нужны только некоторые функции.

Мы можем написать интерфейсы заранее, посмотреть, как они будут себя вести (например, через Unit-тест), а только потом писать основной код. Это уберегает от глупых ошибок.

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

Пример использования:

// Функции для работы со свойствами "Человека"
interface Person {
    function firstName();
    function lastName();
}

// Функции для работы со свойствами "Водителя"
interface Driver extends Person {
    function license();
}

// Функции для преобразования объекта в строку
interface Stringify {
    function __toString();
}

// Функция ожидает на вход любой объект, который реализует интерфейс "Человек"
function personName(Person $person) {
    return $person->lastName() . ' ' . $person->firstName();
}

// Функция ожидает на вход любой объект, который реализует интерфейс "Водитель"
function driverLicense(Driver $driver) {
    return $driver->license();
}

// Класс может реализовывать любое количество интерфейсов
class MyClass implements Driver, Stringify {
    function firstName() {
        return "Иван";
    }
    
    function lastName() {
        return "Петров";
    }
    
    function license() {
        return "AA123";
    }
    
    function __toString() {
        return spring("Driver: %s, License: %s", 
            personName($this),
            driverLicense($this)
        );
    }
}

echo new MyClass;  // Driver: Петров Иван, License: AA123

[Наверх]


Абстрактный класс

Ключевое слово: abstract

Документация: https://www.php.net/manual/ru/language.oop5.abstract.php

Описание:

С помощью абстрактных классов и методов мы перекладываем обязанность "реализовать логику" на кого-то другого. А среда исполнения будет пристально бдить, чтобы все абстрактные методы были реализованы. Нужно помнить, что абстрактные классы всегда предполагают, что их кто-то будет наследовать.

Пример использования:

// Базовый класс фигур
abstract class Shape {
    abstract function name();    // Делегируем функцию потомкам
    abstract function square();  // Делегируем функцию потомкам
    
    function __toString() {
        return sprintf("Фигура: %s, Площадь: %.3f",
            $this->name(),
            $this->square()
        );
    }
}

class Rectangle extends Shape {
    private $width, $height;
    
    function __construct($width, $height) {
        $this->width = $width;
        $this->height = $height;
    }

    function name() {
        return "Прямоугольник";
    }
    
    function square() {
        return $this->width * $this->height;
    }
}

class Circle extends Shape {
    private $radius;
    
    function __construct($radius) {
        $this->radius = $radius;
    }
    
    function name() {
        return "Круг";
    }
    
    function square() {
        return pi() * $this->radius * $this->radius;
    }
}

echo new Rectangle(10, 20), PHP_EOL;  // Фигура: Прямоугольник, Площадь: 200.000
echo new Circle(20), PHP_EOL;         // Фигура: Круг, Площадь: 1256.637

[Наверх]


Перегрузка функций

Ключевое слово: -

Документация: https://www.php.net/manual/ru/language.oop5.overloading.php

Описание:

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

Пример использования:

class A {
    function name() {
        return "Вася";
    }
    
    function greeting() {
        echo "Здравствуй, ", $this->name();
    }
}

class B extends A {
    function name() {
        return "Петя";
    }
}

$obj = new B();
$obj->greeting();  // Здравствуй, Петя

[Наверх]


Финализация

Ключевое слово: final

Документация: https://www.php.net/manual/ru/language.oop5.final.php

Описание:

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

Пример использования:

class A {
    final function foo() {
    }
}

class B extends A {
    function foo() {  // Fatal error: Cannot override final method A::foo()
    }
}

final class C {
}

class D extends C {  // Fatal error: Class D may not inherit from final class (C)
}

[Наверх]


Область видимости свойств и методов

Ключевые слова: public, protected, private

Документация: https://www.php.net/manual/ru/language.oop5.visibility.php

Описание:

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

Существует три типа видимости:

Старайтесь не использовать публичные свойства, в PHP они плохо контролируются, используйте публичные методы.

Пример использования:

class A {
    final private   $firstName  = 'Вася';
    protected $middleName = 'Васильиевич';
    public    $lastName   = 'Васильев';
    
    function name() {
        return join(' ', [
            $this->firstName,
            $this->middleName,
            $this->lastName
        ]);
    }
}

class B extends A {
    private   $firstName  = 'Петя';
    protected $middleName = 'Петрович';
    public    $lastName   = 'Петров';
}

$obj = new B();

// Вызов метода
echo $obj->name();  // Вася Петрович Петров

// Доступ к свойствам
$obj->firstName  = '';  // Error: Cannot access private property
$obj->middleName = '';  // Error: Cannot access protected property
$obj->lastName   = '';  // Без ошибок

[Наверх]


Статичные методы и переменные

Ключевые слова: static, self

Документация: https://www.php.net/manual/ru/language.oop5.static.php

Описание:

Мы используем статичные переменные и методы, если хотим сделать их глобальными, а не привязанными к отдельным объектам. В статичных методах нельзя использовать $this.

Пример использования:

class A {
    static public $myVar = 123;
    
    static function greeting() {
        echo "Привет, ", self::$myVar, "!", PHP_EOL;
    }
}

A::$myVar = "мир";
A::greeting();  // Привет, мир!

$obj = new A();
A::$myVar = "Вася";
$obj->greeting();  // Привет, Вася!

[Наверх]


Позднее связывание

Ключевое слово:: static

Документация: https://www.php.net/manual/ru/language.oop5.late-static-bindings.php

Описание:

Мы используем позднее связывание, чтобы перегружать статичные свойства и методы, поскольку self всегда ссылается только на текущий класс.

Пример использования:

class A {
    static public $name = "Вася";
    
    static function test() {
        echo 'self::$name ', self::$name, PHP_EOL;
        echo 'static::$name ', static::$name, PHP_EOL;
    }
}

class B extends A {
    static public $name = "Петя";
}

B::test();  // self::$name Вася
            // static::$name Петя

[Наверх]


Шаблоны проектирования (паттерны)

Ключевое слово: pattern

Зачем нужно: типовые решения типовых проблем.

Документация: https://ru.wikipedia.org/wiki/Шаблон_проектирования

Описание:

Мы используем шаблоны проектирования, чтобы не изобретать "велосипед". Большинство задач, с которыми программисты сталкиваются каждый день, уже были решены много раз до этого. Наиболее правильные решения были описаны на разных языках программирования и названы "шаблонами проектирования". Их нужно знать, чтобы, как минимум, понимать, о чём идёт речь в разговорах с программистами.

Использование:

<Ведущий разработчик>: А вот здесь надо использовать декомпозицию и реджистри.

        <Программист>: (многозначительно кивает)

<Ведущий разработчик>: Сюда хорошо встанет обсервер.

        <Программист>: (делает вид, что понимает)

<Ведущий разработчик>: Вопросы есть?

        <Программист>: Нет, тут же всё просто!
        
<Ведущий разработчик>: (уходит в закат)

        <Программист>: (с двойным усердием продолжает писать херню)

[Наверх]


Принципы SOLID

Ключевое слово: solid

Зачем нужно: принципы разработки качественных приложений.

Документация: https://ru.wikipedia.org/wiki/SOLID_(объектно-ориентированное_программирование)

Описание:

Мы используем слово SOLID, когда хотим показать свою важность и крутость. Всё дело в том, что на практике мало кто разбирается, что это такое и как оно должно работать. Но, если программист говорит, что не знает SOLID, то в него принято кидать какашки.

В SOLID есть 5 базовых принципов:

  1. Single responsibility - принцип единой ответственности. Он запрещает нам делать золотые молотки (когда в один класс запихивают что надо и не надо).
  2. Open-closed - принцип открытости-закрытости. Он запрещает нам делать публичные свойства и методы, если они нужны только внутри класса.
  3. Liskov substitution - принцип подстановки Барбары Лисков. Этот принцип гласит, что потомки должны иметь как минимум тот же интерфейс, что и родитель. Благо, среда исполнения строго следит за соблюдением этого принципа.
  4. Interface segregation - принцип разделения интерфейса. Он запрещает нам делать золотые молотки из интерфейсов. Два маленьких интерфейса лучше, чем один большой.
  5. Dependency inversion - принцип инверсии зависимостей. Он советует нам в качестве типов входных аргументов в функциях использовать интерфейсы, а не классы. Интерфейс можно реализовать в любом классе, а поменять базовый класс можно далеко не всегда.

Использование:

<Ведущий разработчик>: Почему ты не применил тут декомпозицию и реджистри?

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

<Ведущий разработчик>: А обсервер-то почему не сделал?

        <Программист>: Это нарушило бы принцип единой ответственности.
                       Должна быть только одна причина для изменения класса.

<Ведущий разработчик>: (впал в задумчивость)

        <Программист>: (делает умный вид)
        
<Ведущий разработчик>: Ты прав! Мы не должны нарушать принципы SOLID.

        <Программист>: Именно, ведь мы серьёзные разработчики.

<Ведущий разработчик>: (довольный уходит в закат)

        <Программист>: (лениво дописывает херню)

[Наверх]

Хорошая статья, мне понравилась. Оставлю отзыв!