Первая буква S из SOLID

Single Responsibility Principle — таким образом расшифровывается первая буква из аббревиатуры SOLID.

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

Пример нарушения этого принципа.
Например мы парсим результаты ответа с социальной сети инстаграм, по определенному хештегу, у нас есть класс.
P.S.: приведен псевдокод, применять его в реальной жизни не стоит

Например наш класс имеет такой вид

<?php
class ParseRequest {
    private $guzzle;
    public function __construct(Guzzle $guzzle)
    {
        $this->guzzle = $guzzle;
    }
    
    public function parse($url)
    {
        $data = $this->guzzle->get($url);
        return $this->getUsersLastPhotos($data);
    }
    
    public function getUsersLastPhotos($data)
    {
        // Собирает последние фото у пользователей инстаграма
        // из результата запроса
    }
}
?>

Клиентский код

<?php
$url = 'instagram.com/search?q=#какойтохештег';
$parse = new ParseRequest(new GuzzleHttp\Client());
$usersLastPhotos = $parse->parse($url);
?>

Грубый пример, но сразу видно что класс
отвечает за разбор запроса НО как именно будет разбираться этот запрос — гвоздями прибито в этом классе, что в принципе делает две задачи на один класс, это — КАК мы будем получать данные, и что мы будем с ними делать декомпозицию задачи и разбиение её на более конкретные части никто не отменял.

Попробуем разбить семантически, зоны ответственности в виде интерфейсов

interface IResource {
    public function getResource();
}

interface IHandlerResource {
    public function handle(array $data=[]);
}

Определим два интерфейса:

1. IResource — говорит нам, реализуй откуда ты возьмешь ресурс
2. IHandlerResource — говорит нам как и что ты будешь делать с этим ресурсом

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

class InstagramResource implements IResource {
    
    protected $url;
    private   $guzzle;
    protected $options;
    
    public function __construct(Guzzle $guzzle){
        $this->guzzle = $guzzle;
    }
    
    public function getResource(){
       return $this->guzzle->request($this->url, $this->options);
    }
    
}

Пример модифицирования наследованием:
Здесь опишем пример того что будем получать ресурс из хештега, ну то есть через поиск

class ByHastagResource extends InstagramResource {
    
    protected $url = 'instagram.com/search?q=#somehastag';
    
    public function __construct(Guzzle $guzzle){
        parent::__construct($guzzle)
    }
    
}

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

class InstagramLastPhotoHandler implements IHandlerResource {
    
    public function handle(array $data){
        return $this->lastPhotos($data);
    }
    
    public function lastPhotos($data){
        return // массив с последними фото пользователей
    }
}

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

class InstagramParser {
    
    private $resource;
    private $handler;
    
    public function __construct(IResource $resource, 
                                IHandlerResource $handler)
    {
        $this->resource = $resource;
        $this->handler = $handler;
    }
    
    public function parse()
    {
        return $this->handler
        ->handle($this->resource->getResource());
    }
}

Клиентский код

$instagramResource = new ByHastagResource(new Http\GuzzleClient());
$handler = new InstagramLastPhotoHandler();

$instagramParser = new InstagramParser($instagramResource, $handler);
$lastPhotos = $instagramParser->parse();

var_dump($lastPhotos); // Результатом будет отсортированный массив данных с последними фотографиями пользователей исходной выборки

Декларируя интерфейсы, можно таким образом проектировать верхние уровни, но это не просто, точнее к этому нужно привыкнуть.

Итог:
Ответственности разделены на получение ресурса путем реализации интерфейса IResource. И то, что мы будем делать с этим ресурсом было описано интерфейсом IHandlerResource.

Придерживаясь принципа (Single Responsibility Principle)  получаем конкретные реализации, а так же получаем гибкость в плане разной реализации и подхода для решения задачи

P.S.: Код местами утрирован и является показательным примером.

Loading

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *