Эпизод 3. S – SRP: The Single Responsibility Principle

Привет всем в 3-ем эпизоде сериала, где мы будем разбирать 1-ый из 5-ти принципов SOLID. Прежде чем прочесть эту часть, прочтите предыдущую(если не читали).

Теория

“Принцип единственной ответственности”, – в книге Мартина говориться: “Модуль ответственен только перед одним актером”. В современном прочтении же, звучит так: каждый метод должен иметь одну ответственность(делать одно действие или иметь причину для изменения – кому как угодно). Сам принцип был “разработан” Мейлиром Пейдж-Джонсом и  Томом ДеМарко – инженер-программистами.

Примеры

№1: на проекте сидят трое: менеджер по клиентам, системный администратор и кодер. Сис.админ и менеджер не могут заглядывать в код, навалянный кодером. Кодер не должен общаться с клиентом и администрировать сервер(и это моя позиция, но на всех галерах, где я работал – это нарушалось); ну и т.д.

№2: первобытный общественный строй – все занимаются всем дабы выжить. Пришла идея, чтобы каждый делал то, что у него получается лучше, и делился излишками своего дела; так поступают все – и вуаля! появляются искусства и прочие не необходимые для выживания виды деятельности. А все почему? – потому, что появляются излишки производства, т.к. каждый делает то, что умеет и могет.

Практика

В прошлой статье приведен класс, делающий все, от парсинга УРЛ до отрисовки страницы. Произведем “разделение труда” в классе “первобытного строя”.

Вначале мы парсим УРЛ и извлекаем идентификатор продукта с него – пусть этим будет занят класс Parser

<?php
class Product
{
    // достать инфо  о продукте
    // дергаем Currency и пересчитываем цену продукта
}

class Parser
{
    // парсить URL
    // достать ID продукта
}

class Currency
{
    // достать информацию о символе валюты
    // достать информацию о цене валюты
}

class View
{
    // написовать страницу
}

class Controller
{
    // дергаем Parser и получаем ID продукта
    // дергаем Product получаем инфо о продукте
    // дергаем View и отдает ему все на рендеринг
}

Затем, как Parser обработал УРЛ, дернем Product и получим данные о нем. Product, в свою очередь, обратится к Currency и получить инфу о валюте клиента; пересчитает цену товара в клиентских попугаях. Итак, информация собрана – отдадим все это на рендеринг(классу View).

Да, при этом появляется класс-дирижёр(Controller). Вероятно, правильнее было б работать с Currency через него – пишите в комменты…

В общем – это мое виденье данного, я б сказал главного, принципа кодирования.

Эпизод 2. СОЛИДный код

Рад приветствовать вас во втором эпизоде сериала “ООП здорового человека”. В этой серии: мы узнаем(если кто-то еще не знает) о главных принципах ООПешного кодирования, которые ложатся в аббревиатуру с аббревиатур SOLID. Но, поскольку я не согласен, что эти 5 принципов основные – рассмотрим еще принципы.

SOLID

Вот, что нам говорит страница Википедии: “SOLID — мнемонический акроним, введённый Майклом Фэзерсом для первых пяти принципов, названных Робертом Мартином в начале 2000-х, которые означали 5 основных принципов объектно-ориентированного программирования и проектирования”. Я не филолог и не историк – потому приму это утверждение на веру(как по мне – здесь важно только то, что это набор принципов…). Посмотрев много источников, хочу сказать, что авторство принцыпов принадлежит не Робу Мартину; он всего лишь пересказал их – некоторые перевел на более доступный язык, некоторые переосмыслил(и скажу, что в его интерпретации они стали лучше) ну и т.д… ах-да, еще и популяризировал! – что немаловажно…

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

Теперь, сами принципах:

  • S – SRP: The Single Responsibility Principle
  • О – OCP: The Open Closed Principle
  • L – LSP: The Liskov Substitution Principle
  • I – ISP: The Interface Segregation Principle
  • D – DIP: The Dependency Inversion Principle

Практическая часть

Кроме теории, я буду в коде(на моем любимом PHP) разбирать как воплотить эти принципы. Для этого я подготовил следующий god-объект(объект-бог):

<?php
class Page
{
    // достаем ID продукта из URL
    // достаем данные о продукте
    // достаем данные о валюте
    // пересчитываем цену продукта
    // рисуем страницу продукта
}

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

  • получить идентификатор продукта;
  • информацию о нем;
  • информацию о валюте покупателя;
  • расчет цены продукта в валюте покупателя;
  • рендер продуктовых данных в шаблон.

Сами методы реализовывать я не буду(не это цель, но посмотрим…) – всего-лишь продемонстрирую “расчесывание” класса согласно этим 5-ти принципам.

Смысл

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

Эпизод 1. Механизмы

Здравствуйте всем, это первая серия сериала “ООП здорового человека”. Есть 3 механизма, без которых ООП не ООП – это наследование, инкапсуляция и полиморфизм. Также, есть механизм “сокрытие”, который приписываю к части инкапсуляции.

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

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

<?php
class Person
{
    public int $id;
    public string $name;
    public string $secondName;
    public string $sex;
    public string $birthday;

    public function toArray()
    {
        return get_object_vars($this);
    }
}

допустим персона это частный случай каких-то единиц(персона наследует элемент)

<?php
class Item
{
    public int $id;
    public string $name;

    public function toArray()
    {
        return get_object_vars($this);
    }
}

class Person extends Item
{
    public string $secondName;
    public string $sex;
    public string $birthday;
}

При этом класс Person включает все методы и свойства класса Item

<?php
// классы ранее

print_r(get_class_methods('Person'));
print_r(get_class_vars('Person'));

// вывод
Array
(
    [0] => toArray
)
Array
(
    [secondName] => 
    [sex] => 
    [birthday] => 
    [id] => 
    [name] => 
)

Однако, их(активы наследуемого класса) можно скрыть от наследника – об этом в следующей статье. Смысл наследования состоит в минимальном копи-пасте кода. В некоторых языках наследоваться можно от многих, в других же – от одного класса(пример PHP – там множественное наследование спародировано трейтами – об этом в следующих сериях).

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

Инкапсуляция (англ. encapsulation, от лат. in capsula) — размещение в одном компоненте данных и методов, которые с ними работают. В реализации большинства языков программирования обеспечивает механизм сокрытия, позволяющий разграничивать доступ к различным компонентам программы. Этот принцип в классе Person сохранен, т.к. все методы класса работают с данными, лежащими в нем же.

Инкапсуляция считается неполной если не реализовано сокрытие. Сокрытие – запрет на прямой доступ к полям объекта, вместо этого – доступ через модификаторы. На примере класса Item это выглядит так

<?php
class Item
{
    protected int $id;
    protected string $name;

    public function setId($id)
    {
        $this->id = (int) $id;
    }

    public function getId()
    {
        return $this->id;
    }

    public function setName($name)
    {
        $this->name = strval($name);
    }

    public function getName()
    {
        return $this->name;
    }

    public function toArray()
    {
        return get_object_vars($this);
    }
}

Для сокрытия модификатор (свойства) public заменяется на protected/private(о разнице также в следующих сериях) и доступ открывается через “сеттеры” и “геттеры”(собственно, модификаторы).

Полиморфизм

И снова Википедия: “Полиморфизм в языках программирования и теории типов — способность функции обрабатывать данные разных типов”. Если мы рассмотрим класс Item(вариант с сокрытием), то метод setId(setName также) имеет в себе реализацию полиморфизма, т.к. принимает в себя параметр различных типов и приводит его к нужному.