Три слона на которых стоит ООП

Три слона объектно-ориентированного программирования

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

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

Инкапсуляция

Инкапсуляция позволяет нам скрывать методы и свойства класса, изменения которых доступны через специальный интерфейс. Например инкапсулировать проперти и методы мы можем сделав их приватными или защищенными (protected), во втором случае доступ будет предоставлен так же потомкам класса (читайте наследование ниже). Благодаря инкапсуляции мы можем ограничивать доступ к некоторому содержимому класса, тем самым заставлять пользователя использовать данные по определенным правилам, описанным в публичных методах, которые дают доступ к этим данным.

<?php

class A
    {
        private $_a;
        private $_b;
        public $c;

        private function _setPropetries(int $a, string $b)
            {
                $this->_a = $a;
                $this->_b = $b;
            }
    
        public function setup(int $a, string $b)
            {
                $this->_setProperties($a, $b);
            }
    }

?>

Класс “A” инкапсулирует свойства $a и $b а так же метод _setProperties(), доступ к изменению данных осуществляется через метод setup(), свойство $c не инкапсулировано и доступно извне. При этом я использую жесткую типизацию для соответствия типов данных.

Наследование

Наследование – это расширение функционала класса путем создания его потомка. Каждый класс может иметь неограниченное количество наследников (потомков, чайлдов – “child”), но лишь одного родителя (перента – “parent”). Например класс фрукт является родителем классов: банан, апельсин, яблоко и тд., а они являются его детьми (потомками). Вы можете создавать массу подтипов класса, для разделения функционала. К примеру у вас есть базовый класс хранилище, который сохраняет данные, он абстрактный и описывает какие методы должны быть в классе потомке, какие аргументы будут на входе и что будет на выходе. После вы можете расширить класс “хранилище”, сделать его extend, создав классы хранилище в базе данных MySQL, хранилище в файлах XML, хранилище на другом сервере передавая данные по REST API и тд. То есть вы можете создать массу расширений для базового класса “хранилище”.

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

Пример наследования в PHP:

<?php
class A
    {
        protected $a;

        public function addString(string $a)
            {
                $this->a .= $a;
            }
    }

class B extends A
    {
        public function erase()
            {
                $this->a = "";
            }
    }


?>

Явный пример наследования и инкапсуляции. Класс “А” имеет защищенное свойство “$a”, которое не доступно из вне, а изменяется только через метод addString(), к значению свойства добавляется строка в конце. Но базовый класс не умеет ее стирать, а вот потомок класса “А”, класс “В” унаследовал все свойства и методы класса “А”, при этом имеет собственный метод erase() для стирания данных из свойства $a, которое он унаследовал от своего родителя. Тем самым используя класс “В”, мы имеем доступ к методу addString() и методу erase(), что значительно изменило функционал класса. Простой и понятный пример наследования.

Полиморфизм

Полиморфизм – третий слон на котором стоит объектно-ориентированное программирование. Представьте, у нас есть базовый абстрактный класс “Животное”, у него есть некий метод “говорить” – speak(), который возвращает строку со звуком животного, у каждого животного свой звук, у расширения (потомка) “собака” будет: “гав”, у котенка: “мяу”, у утки: “кряк”. Все эти классы: кошка, собака, утка являются расширениями класса “животное”, они одновременно являются типами “животное” и типом себя. То есть мы можем сделать базовый интерфейс, который будет принимать на вход все классы типа “животное” и просить внутри этого интерфейса метод “говорить”, вернув его значение, а передать мы туда сможет не только класс “животное”, но и его потомков.

Рассмотрим пример полиморфизма на PHP:

<?php

abstract class Animal
    {
        abstract public function speak():string;
    }

class Dog extends Animal
    {
        public function speak():string
            {
                return "Woof!";
            }
    }

class Cat extends Animal
    {
        public function speak():string
            {
                return "Meow!";
            }
 }

class Speaker
    {
        public function start(Animal $animal):string
            {
                return $animal->speak();
            }
    }

?>

Если мы создадим инстанцию класса Speaker и передадим в его метод start() инстанцию любого из классов, Cat или Dog мы получим строку со звуком животного, на входе в функцию start() у нас указано, что аргумент $animal должен быть типом Animal, но так как Cat и Dog являются потомками (extends, расширениями) класса Animal они являются так же типом Animal помимо собственных типов Cat и Dog. Из статьи про типы данных мы помним, что класс тоже является типом данных в PHP.

Нет ничего сложного в использовании полиморфизма, главное понять, что это такое, из примера выше вы увидели, что полиморфизм в PHP – это возможность создания подтипа класса путем его расширения, создания его потомков.

Заключение

Данная статья является ключевой, так как любой профессиональный программер должен понимать и уметь использовать основы ООП, а именно речь идет об инкапсуляции, наследовании и полиморфизме. Я доступным языком и на простых примерах рассказал вам, что это такое и с чем это едят. Далее я хотел бы познакомить вас с основными принципами разработки программ, такими как SOLID, DRY, KISS, WET и др. В следующих статьях мы о них поговорим.

Подпишитесь на рассылку новых статей

Подпишитесь на рассылку свежих статей и присоединяйтесь к 7 остальным подписчикам.