dqyl2374 2013-01-29 16:54
浏览 31
已采纳

MVC和依赖注入,被迫使用singleton Controller?

I'm working on building a PHP framework that behaves according to MVC principles and utilizes dependency injection. I think I have the front-controller part down; there is a working router that instantiates a controller instance and calls the appropriate action based on the requested URI.

Next up is dependency injection. I want to implement a Container that resolves dependencies using reflection. In doing so, I think I'm running into a problem with my controllers.

There are a number of what I call "system dependencies" that need to be available to derived controller classes. I haven't actually created all these dependencies yet, but it seems sensible that controllers have access to services like an InputProvider (to encapsulate get/post params or command line arguments), and maybe an Output dependency. Ideally, I'd use the framework's Container to inject these dependencies into the constructor of the controller - but this is where I run into problems.

If I use constructor injection for system dependencies in the controller, then I'm forcing derived controllers to manage the base controller's dependencies if they implement a constructor of themselves. That doesn't seem to be the most user-friendly. The other option is to use setter injection for system dependencies, but then derived controllers won't have access to these system dependencies if they should have need of them in their constructor.

The only solution I see that offers the best of both worlds is to make my controllers singletons. They'd have a private constructor so I can safely use setter injection without worrying about the constructors of derived classes. Instead, there would be an overridable initialize() method (assuming I get method injection working somehow), that basically fulfills the role of constructor (as in, an initializer for the derived controller). This way, constructor injection is replaced by method injection in the initialize() method, where all system dependencies would be available, without requiring the derived controller to manage them.

But then, a quick google search seems to unanimously say that singleton controllers are bad practice. I'm very unsure on how to proceed. I am probably overengineering this, but apart from wanting my application to be future-proof and maintainable, I also see it as a little exercise in applying best practices, so I'd like to do things "properly".
I think the best practice in this case would be to pass the responsibility of managing the required system dependencies to the derived controller. A dependency should probably only be instantiated if the derived controller actually has need of it. Why inject an InputProvider in the base controller if it is possible that a derived controller is never even going to use it? But at the same time, I keep coming back to user friendliness, and how nice it is to simply always have a $this->input member available, such as in a framework like CodeIgniter.

I highly appreciate any and all contributions to my dillemas. I also apologise for the wall of text, but I couldn't think of any code examples that would make the job of explaining any easier, since it's all so abstract to me right now!

Sincerely,
A severely torn individual

  • 写回答

1条回答 默认 最新

  • dongyun3897 2013-02-01 11:28
    关注

    Several solutions are possible:

    • forbid controllers to use __construct(): make it private public final, and make controllers override something like init() and call it from the constructor. Then the constructor would inject all the dependencies (reflection? other stuff?), so that they are all ready in init().

    • you can use an existing DI library like PHP-DI (disclaimer: I work on that) that will allow you to define dependencies but have them available in the constructor (magically, yes).

    Something like that:

    <?php
    use DI\Annotations\Inject;
    
    class FooController {
        /**
         * @Inject
         * @var Bar
         */
        private $bar;
    
        public function __construct() {
            // The dependency is already injected
            $this->bar->sayHello();
        }
    
        public function setBar(Bar $bar) {
            return $this->bar = $bar;
        }
    }
    

    For example that is how I work with Zend Framework 1. I can't use the constructors, so I inject into properties. Here is the ZF1 integration project.

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

报告相同问题?

悬赏问题

  • ¥15 各位请问平行检验趋势图这样要怎么调整?说标准差差异太大了
  • ¥15 delphi webbrowser组件网页下拉菜单自动选择问题
  • ¥15 wpf界面一直接收PLC给过来的信号,导致UI界面操作起来会卡顿
  • ¥15 init i2c:2 freq:100000[MAIXPY]: find ov2640[MAIXPY]: find ov sensor是main文件哪里有问题吗
  • ¥15 运动想象脑电信号数据集.vhdr
  • ¥15 三因素重复测量数据R语句编写,不存在交互作用
  • ¥15 微信会员卡等级和折扣规则
  • ¥15 微信公众平台自制会员卡可以通过收款码收款码收款进行自动积分吗
  • ¥15 随身WiFi网络灯亮但是没有网络,如何解决?
  • ¥15 gdf格式的脑电数据如何处理matlab