dongzine3782 2019-01-21 19:05 采纳率: 0%
浏览 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 乌班图ip地址配置及远程SSH
  • ¥15 怎么让点阵屏显示静态爱心,用keiluVision5写出让点阵屏显示静态爱心的代码,越快越好
  • ¥15 PSPICE制作一个加法器
  • ¥15 javaweb项目无法正常跳转
  • ¥15 VMBox虚拟机无法访问
  • ¥15 skd显示找不到头文件
  • ¥15 机器视觉中图片中长度与真实长度的关系
  • ¥15 fastreport table 怎么只让每页的最下面和最顶部有横线
  • ¥15 java 的protected权限 ,问题在注释里
  • ¥15 这个是哪里有问题啊?