dongzine3782 2019-01-21 19:05
浏览 239
已采纳

Laravel 5.5:我是否在正确模式下使用依赖注入?

I did write a simple Interface under app\Libs. I registered the namespace in composer.json

"autoload": {
        "classmap": [
            "database/seeds",
            "database/factories",
            "app/Libs"
        ],

DomoticControllerInterface.php (the name "Controller" because this is a controller for domotic setup, not the "Laravel Controller ;)"

namespace App\Libs;

interface DomoticControllerInterface
{
    /**
     * Get the current temperature.
     */
    public function getCurrentTemperature();

    /**
     * Get the current status of general heating (ON/OFF)
     */
    public function getCurrentStatusOfGeneralHeating();

}

And a class Domoticz.php that implements it

<?php
/**
 * Domoticz instance.
 *
 * @see www.domoticz.com
 */

namespace App\Libs;


class Domoticz implements DomoticControllerInterface
{

    public function getCurrentTemperature()
    {
        // TODO: Implement getCurrentTemperature() method.
        return "27.4";
    }

    public function getCurrentStatusOfGeneralHeating()
    {
        // TODO: Implement getCurrentStatusOfGeneralHeating() method.
    }
}

I wrote a HeaterService.php (model? Provider?) under app\Libs

<?php
/**
 * Heater Service Class.
 * This class perform work on Heater
 *
 * @since 3.0.0
 * @author sineverba
 */

namespace App\Libs;


/**
 * Class HeaterService
 * @package App\Libs
 */
class HeaterService
{

    /**
     * The domotic controller.
     *
     * @var object
     */
    private $domotic_controller;

    public function __construct($domotic_controller)
    {
        $this->setDomoticController($domotic_controller);
    }

    /**
     * Get the current temperature
     *
     * @return string the current temperature
     */
    public function getCurrentTemperature()
    {
        return $this->getDomoticController()->getCurrentTemperature();
    }

    /**
     * Set the domotic controller.
     *
     * @param object the domotic controller to use
     * @since 1.0.0
     * @author sineverba
     */
    private function setDomoticController($domoticController)
    {
        $this->domotic_controller = $domoticController;
    }

    /**
     * Get the istance of domotic controller
     *
     * @return object the domotic controller
     * @since 3.0.0
     * @author sineverba
     */
    private function getDomoticController()
    {
        return $this->domotic_controller;
    }

}

Finally, in my BaseController.php under app\Http\Controllers

<?php

/**
 * Base Controller that redirect to right controller/model,
 * based on the URL params.
 *
 * @author sineverba
 * @since 1.0.0
 */

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class BaseController extends Controller
{
    //
    /**
     * Get the param from the URL and redirect to right controller.
     *
     */
    public function getTheScriptFromUrl(Request $request)
    {

        $domotic_controller = env("DOMOTIC_CONTROLLER", "Domoticz");
        if ($domotic_controller=="Domoticz") {
            $dom_controller = new \App\Libs\Domoticz();
        }
        $heater = new \App\Libs\HeaterService($dom_controller);

        echo $heater->getCurrentTemperature();
    }
}

It works. I got the 27.4 hardcoded.

But, is this the right mode to use?

Another one question, I would use the Type-Hinting in HeaterService.php constructor, but don't know how to do.

Finally, my app works (for the moment), without binding. Is it possible?

Thank you very much

  • 写回答

1条回答 默认 最新

  • doumi1944 2019-01-21 19:20
    关注

    There are several things wrong here. So let's take them one at a time.

    First of all, there's no need to autoload the class if you are adding it under the app namespace. It should already be auto-loaded.

    "autoload": {
        "psr-4": {
            "App\\": "app/"
        }
    }
    

    A very powerful feature of the service container is its ability to bind an interface to a given implementation.

    In OOP, you will often hear the phrase "code to an interface", and that's what's happening here.

    With that in mind, let's create a HeaterServiceProvider. Its responsibility will be to register our dependency into the container so that we can use it later.

    <?php
    
    namespace App\Libs;
    
    use Illuminate\Support\ServiceProvider;
    
    class HeaterServiceProvider extends ServiceProvider
    {
        /**
        * Register bindings in the container.
        *
        * @return void
        */
        public function register()
        {
            $this->app->bind(
                'App\Libs\DomoticControllerInterface',
                'App\Libs\Domoticz
            );
        }
    }
    

    ... and then you would register this service provider in your config/app.php's providers array.

    'providers' => [
        // 
        //
        App\Libs\HeaterServiceProvider::class
    ]
    

    This will register the bindings into your application's container. Wherever you need to resolve App\Libs\DomoticControllerInterface, an instance of App\Libs\Domoticz will be returned.

    So, to use it, you could just type-hint the interface in your methods, such as:

    <?php
    
    /**
    * Base Controller that redirect to right controller/model,
    * based on the URL params.
    *
    * @author sineverba
    * @since 1.0.0
    */
    
    namespace App\Http\Controllers;
    
    use Illuminate\Http\Request;
    use App\Libs\DomoticControllerInterface;
    
    class BaseController extends Controller
    {
        //
        /**
        * Get the param from the URL and redirect to right controller.
        *
        */
        public function getTheScriptFromUrl(DomoticControllerInterface $heater, Request $request)
        {
            // Laravel will automatically resolve an instance of 
            // App\Libs\Domoticz here, so you could just do the following
            echo $heater->getCurrentTemperature();
        }
    }
    

    In your particular case, by "newing" up your implementation inside the getTheScriptFromUrl(), you are actually "coding to an implementation" rather than an interface.

    You are also defeating the purpose of using binding things to the container if you are not actually going to resolve your dependencies from it.

    Your implementations may change in the future. If you code to an interface, the only thing you'd need to do if your implementations change would be to bind the new implementation into the container and your application should be good to go.

    Also, it's much easier to perform testing if you are using Dependency Injection. You could easily swap/mock those implementations in a test environment.

    I would suggest you to take a deeper look at the Service Container documentation. You can also take a look at this article. The author does a really good job in explaining Laravel's Service Container.

    Additionally, going through the Service Provider's documentation would also help.

    Edit: Based on our discussion in the comments, here's how you would do it in your scenario.

    <?php
    
    namespace App\Libs;
    
    use Illuminate\Support\ServiceProvider;
    
    class HeaterServiceProvider extends ServiceProvider
    {
        /**
        * Register bindings in the container.
        *
        * @return void
        */
        public function register()
        {
            $implementation = 'Homeassistant'; // You could be pulling this from anywhere
            $this->app->bind(
                'App\Libs\DomoticControllerInterface',
                "App\Libs\{$implementation}"
            );
        }
    } 
    

    The point is, you don't actually need to create an instance of your implementation anywhere since you are already binding it in your controller.

    In our particular case, the following:

    <?php
    
    /**
    * Base Controller that redirect to right controller/model,
    * based on the URL params.
    *
    * @author sineverba
    * @since 1.0.0
    */
    
    namespace App\Http\Controllers;
    
    use Illuminate\Http\Request;
    use App\Libs\DomoticControllerInterface;
    
    class BaseController extends Controller
    {
        //
        /**
        * Get the param from the URL and redirect to right controller.
        *
        */
        public function getTheScriptFromUrl(DomoticControllerInterface $heater, Request $request)
        {
            // Laravel will automatically resolve an instance of 
            // App\Libs\Homeassistant here, so you could just do the following
            echo $heater->getCurrentTemperature();
        }
    }
    

    I hope that makes sense :)

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 对于这个复杂问题的解释说明
  • ¥50 三种调度算法报错 采用的你的方案
  • ¥15 关于#python#的问题,请各位专家解答!
  • ¥200 询问:python实现大地主题正反算的程序设计,有偿
  • ¥15 smptlib使用465端口发送邮件失败
  • ¥200 总是报错,能帮助用python实现程序实现高斯正反算吗?有偿
  • ¥15 对于squad数据集的基于bert模型的微调
  • ¥15 为什么我运行这个网络会出现以下报错?CRNN神经网络
  • ¥20 steam下载游戏占用内存
  • ¥15 CST保存项目时失败