如何解决PHP中编写Controller 类的问题,应该是:
向下看,对于使用依赖注入框架的控制器实例化
问题是,派生的控制器可能使用程序员想要的任何资源(例如框架提供)。如何创建对共享资源(数据库,用户,存储,缓存,帮助程序),用户定义的类或其他库的统一访问?
我的问题有几种可能的解决方案,但两者都不是一个优雅的
$controller->setApplication($app)
User::getInstance()
或Database::getInstance()
我明白,创建强耦合类是不鼓励和放逐的:)但是我不知道这个范例如何适用于其他程序员(Controller类)的起点,他们应该能够访问它们提供给MVC架构的共享资源。我相信,将控制器类分解为更小的类会以某种方式破坏MVC的实际意义。
DI Framework看起来是一个可行的选择。但问题仍然存在。类似Controller的类不在Application层中,而是在RequestHandler / Response层中。
该图层应如何实例化控制器?
答案 0 :(得分:3)
您是自己开发框架吗?如果没有,则您的问题不适用,因为您必须从already existing frameworks及其现有解决方案中进行选择。在这种情况下,你的问题必须重新制定,如“如何在框架X中进行单元测试/依赖注入”。
如果您正在自己开发框架,则应首先检查现有框架是如何解决此问题的。您还必须详细说明自己的要求,然后再使用最简单的解决方案。没有要求,你的问题纯粹是审美和争论。
Imho最简单的解决方案是将公共属性初始化为框架提供的默认值,否则可以在此处注入模拟。 (这相当于你的getter / setter解决方案,但是没有提到的膨胀。你并不总是需要getter和setter。)或者,如果你真的需要它,你可以提供一个构造函数来在一次调用中初始化它们(如你所建议的那样)单身人士是一个优雅的解决方案,但你必须问自己,它是否适用于你的情况?如果您的应用程序中必须有相同类型对象的不同实例,则不能使用它(例如,如果您希望仅在应用程序的一半中模拟一个类)。
当然,拥有所有选项真的很棒。您可以使用getter / setter,构造函数,并且在省略初始化时,默认值来自单件工厂。但是在不需要时有太多选项并不是很棒,因为程序员必须弄清楚要使用哪种约定,选项和模式,这是令人不安的。我绝对不想做出几十个设计决定只是为了让一个简单的CRUD运行。
如果你看看其他框架,你会发现没有银弹。通常,单个框架根据上下文使用不同的技术。在控制器中,DI是一个非常简单的事情,看看CakePHP的$ helpers,$ components变量,它们指示将适当的变量注入控制器类。对于应用程序本身而言,单例仍然是一件好事,因为总是只有一个应用程序。使用公共属性注入较少经常更改/模拟的属性。 对于MVC,子类化也是完全可行的选项:就像CakePHP中的AppController,AppView,AppModel一样。它们被插入到框架和所有特定Controller,View和Model类之间的类层次结构中。通过这种方式,您只需要为主要类类型声明全局变量。
在Java中,由于动态类加载器和反射,您可以选择更多选项。但另一方面,您还必须支持更多要求:并行请求,共享对象以及工作线程,分布式应用服务器等之间的状态。
如果您首先知道自己需要什么,那么您只能回答适合自己的问题。但实际上,为什么你还要编写另一个新框架呢?
答案 1 :(得分:1)
当依赖注入可行时,单身人士不赞成(我还没有找到需要单身人士的情况)。
您很可能控制控制器的实例化,因此您可以使用上面提到的$controller->setApplication($application)
,但如果有必要,您可以使用静态方法和变量(它们对正交性的影响远远小于申请比单身人士);即Controller::setApplication()
,并通过实例方法访问静态变量。
例如:
// defining the Application within the controller -- more than likely in the bootstrap $application = new Application(); Controller::setApplication($application); // somewhere within the Controller class definition public function setContentType($contentType) { self::$application->setContentType($contentType); }
我习惯于分离静态和实例属性和方法(必要时,仍然在类定义的顶部对属性进行分组)。我认为这比单身人士更不笨,因为课程仍然非常紧凑。
答案 2 :(得分:1)
重构怎么样?
当然,这不是您的选择之一,但您声明代码是一个很大程度上耦合的类。为什么不花时间和精力将它重构为更模块化,可测试的组件呢?
答案 3 :(得分:1)
据我了解,您的Application类应该是调度程序。如果是这样,我宁愿使用控制器构造函数来传递Application的实例,这样控制器就会知道谁在调用它。稍后,如果您希望拥有一个不同的Application实例,具体取决于是否从CLI中调用代码,您可以拥有一个ApplicationInterface,Application \ Http和Application \ Cli将实现它,并且一切都很容易维护。
你也可以实现一些工厂模式来获得一个很好的DI实现。例如,请在此处检查createThroughReflection方法:https://github.com/troelskn/bucket/blob/master/lib/bucket.inc.php
我希望这是有道理的。
此致 尼克
答案 4 :(得分:1)
您还可以使用ControllerFatory,您可以在其中提供您的应用程序或路由器/调度程序
你可以调用$ controllerFactory-> createController($ name);
您的应用程序根本不知道如何创建工厂的控制器。由于您可以将自己的ControllerFactory注入DI容器,因此您可以根据控制器管理所需的所有依赖项。
class ControllerFactory {
public function __construct(EvenDispatcher $dispatcher,
Request $request,
ResponseFactory $responseFactory,
ModelFactory $modelFactory,
FormFactory $formFactory) {
...
}
public function createController($name = 'Default') {
switch ($name) {
case 'User':
return new UserController($dispatcher,
$request,
$responseFactory->createResponse('Html'),
$modelFactory->createModel('User'),
$formFactory->createForm('User'),...);
break;
case 'Ajax':
return new AjaxController($dispatcher,
$request,
$responseFactory->createResponse('Json'),
$modelFactory->createModel('User'));
break;
default:
return new DefaultController($dispatcher, $request, $responseFactory->createResponse('Html'));
}
}
}
因此,您只需在DI容器中添加此工厂并将其传递给您的应用程序。 每当您需要一个新的控制器时,您将其添加到工厂,如果需要新的依赖项,您可以通过DI容器将它们提供给工厂。
class App {
public function __construct(Router $router,Request $request, ControllerFactory $cf, ... ) {
...
}
public function execute() {
$controllerName = $this->router->getMatchedController();
$actionName $this->router->getMatchedAction();
$controller = $cf->createController($controllerName);
if(is_callable($controller, $actionName)) {
$response = $controller->$action(request);
$response->send();
}
}
}
这不是生产代码,我还没有测试过,但这就是你将控制器与应用程序分离的方式。请注意,虽然这里有一个错误的耦合,因为我的控制器返回一个响应,我在App中执行响应。但就像我说的那只是一个小例子。
将模型,表单和控制器的工厂传递给各自的父母通常是一个好主意,因为最终会在引导时加载所有对象Graph,这非常糟糕且占用内存。
我知道这个答案已经获得批准,但这是我对该主题的2美分
有一篇关于这个主题的好文章
http://miller.limethinking.co.uk/2011/07/07/dependency-injection-moving-from-basics-to-container/