четверг, 23 ноября 2017 г.

Инверсия зависимостей на примере Yii2

По началу хотел здесь написать о контейнере внедрения зависимостей в Yii2, но немного почитав понял, что гораздо важнее не умение использовать конкретную реализацию контейнера внедрения зависимостей, которая предложена разработчиками Yii2, а уметь применять принцип инверсии зависимостей в совокупности с принципом программирования по контракту.

Зачем это нужно?

У меня есть знакомый, который постоянно мне задает этот вопрос "А зачем все это нужно?". В данном случае принцип инверсии зависимостей необходим для создания слабого зацепления (англ. loose coupling). То есть в вашем коде не будет жесткой зависимости от каких-либо конкретных объектов, потому что логика вашей программы будет завязана на использовании интерфейсов( их же я называю и контрактами ), в случае если объект "Хочет" работать с другим вашим классом, который реализует бизнес-логику, то этот объект должен будет принять правила интерфейса(контракта).

Рассмотрим пример

создадим таблицу Users
CREATE TABLE `users` (
  `id` int(11) NOT NULL,
  `name` varchar(50) NOT NULL,
  `email` varchar(50) NOT NULL,
  `lastname` varchar(50) NOT NULL
);
дальше сгенерируем модель Users c помощью gii, заходим на роут ?r=gii выбираем генератор моделей и если у на есть подключение к БД, вписываем первую букву таблицы "u" и gii сам заполнит необходимые поля, нажимаем "Generate" и идем дальше.
Для того, чтобы отделить бизнес-логику программы от обычных объектов я сделал папку app/models/logic получилась следующая структура файлов и папок:
- app
  - models
    -logic
     - user
       - UserContract.php // Интерфейс
       - UserService.php  // Бизнес логика работы с пользователями
в UserContract.php я поместил следующий код:
<?php

namespace app\models\logic\user;

interface UserContract {
    public function getName();
    public function getLastName();
    public function getEmail();    
}
В UserService.php - следующий:
<?php

namespace app\models\logic\user;

class UserService {
    public function getUser(UserContract $user) {
        return [
            'name' => $user->getName(),
            'email' => $user->getEmail(),
            'lastName' => $user->getLastName(),
        ];
    }
}
Из кода видно, что наша логика работает именно с ИНТЕРФЕЙСОМ! И каждый объект, который "захочет" поучаствовать в предоставлении данных в логику метода UserService::getUser должен реализовать интерфейс/контракт UserConctract.
Конечно этот подход кажется немного утомительным, потому что приходится создавать контракты и постоянно отделять объекты от логики приложения, а ведь частенько хочется просто создать объект и использовать его все время!
Но этот подход помогает улучшить общую архитектуру приложения! И насколько я заметил при использовании инверсии зависимостей часто появляются классы, в именах которых есть слово Service, это говорит именно о том, что абстракция данного класса настолько высока, что ему не важно какие конкретно объекты получают его методы.
В заключении посмотрим как можно использовать созданный нами пример в контроллере:
<?php

namespace app\controllers;

use yii\web\Controller;

class SiteController extends Controller {

    public function actionUserView($id) {
        $user = \app\models\Users::findOne($id);  
        if(empty($user)) { return 'error'; }              
        $userService= new \app\models\logic\user\UserService();    
        $userData = $userService->getUser($user);
        return $this->render('/user/view',compact('userData'));
    }

}
В результате в $userData в виде Мы получаем массив с данными о пользователе!
Еще хочется сказать, что такая разбивка логики очень сильно способствует упрощению тестирования, потому что гораздо легче написать модульный тест для класса UserService , чем мучиться попытками протестировать жесткую логику находящуюся в самом контроллере!

Комментариев нет:

Отправить комментарий

Linux командная строка узнаем оставшееся место

Чтобы посмотреть общую картину того, сколько места осталось в системе можно выполнить команду: df -h Чтобы вывести на экран сколько мес...