在PHP中使用依赖项注入组合控制器类
如何解决用PHP编写控制器类的问题,应该是:在PHP中使用依赖项注入组合控制器类,php,model-view-controller,dependency-injection,instantiation,Php,Model View Controller,Dependency Injection,Instantiation,如何解决用PHP编写控制器类的问题,应该是: 通过使用依赖项注入,可以轻松地进行测试 为终端程序员提供共享对象 提供加载新用户库的方法 向下看,对于依赖项注入框架的控制器实例化 问题是,派生控制器可能会使用程序员想要的任何资源(例如框架提供的资源)。如何创建对共享资源(数据库、用户、存储、缓存、助手)、用户定义类或其他库的统一访问 优雅的解决方案? 对我的问题有几种可能的解决办法,但没有一种看起来是优雅的 是否尝试通过构造函数传递所有共享对象?(即使有10个占位符也可以创建构造函数) 创
- 通过使用依赖项注入,可以轻松地进行测试
- 为终端程序员提供共享对象
- 提供加载新用户库的方法
问题是,派生控制器可能会使用程序员想要的任何资源(例如框架提供的资源)。如何创建对共享资源(数据库、用户、存储、缓存、助手)、用户定义类或其他库的统一访问 优雅的解决方案? 对我的问题有几种可能的解决办法,但没有一种看起来是优雅的
- 是否尝试通过构造函数传递所有共享对象?(即使有10个占位符也可以创建构造函数)
- 创建getter、setter?(膨胀的代码)
$controller->setApplication($app)
- 在共享资源上应用单例<代码>用户::getInstance()或
数据库::getInstance()
- 是否将依赖项注入容器用作控制器内对象共享的单例
- 是否提供一个全局应用程序单例作为工厂?(这一个看起来在php框架中非常常用,但它强烈违反了DI原则和德米特定律)
依赖注入框架 DI框架看起来是一个可行的选择。然而,这个问题仍然存在。类控制器不位于应用程序层,而是位于RequestHandler/Response层 该层应如何实例化控制器?
- 将去离子注入器送入该层
- 作为一个单体的DI框架
- 将隔离DI框架配置仅用于此层,并创建单独的DI注入器实例
$controller->setApplication($application)
,但如果需要,您可以使用静态方法和变量(与单例相比,静态方法和变量对应用程序正交性的危害要小得多);即Controller::setApplication()
,并通过实例方法访问静态变量
例如:
//在控制器中定义应用程序——很可能是在引导中
$application=新应用程序();
控制器::设置应用程序($application);
//控制器类定义中的某个地方
公共函数setContentType($contentType)
{
self::$application->setContentType($contentType);
}
我养成了分离静态和实例属性和方法的习惯(必要时,仍然在类定义的顶部对属性进行分组)。我觉得这比使用单例要简单,因为类仍然非常紧凑。重构如何
虽然这不是您的选项之一,但是您声明代码是一个很大程度上耦合的类。为什么不花点时间和精力把它重构成更模块化、可测试的组件呢?你自己在开发一个框架吗?如果没有,您的问题不适用,因为您必须从现有的解决方案中进行选择。在这种情况下,您的问题必须重新表述为“如何在框架X中进行单元测试/依赖项注入” 如果您自己开发一个框架,您应该首先检查现有框架是如何处理这个问题的。您还必须详细说明您自己的需求,然后使用最简单的解决方案。如果没有要求,你的问题纯粹是审美和辩论 Imho最简单的解决方案是将公共属性初始化为框架提供的默认值,否则您可以在此处注入模拟。(这相当于您的getter/setter解决方案,但没有提到的膨胀。您并不总是需要getter和setter。)或者,如果您确实需要它,您可以提供一个构造函数来在一次调用中初始化它们(如您所建议的) 单例是一个优雅的解决方案,但您必须再次问自己,它是否适用于您的情况?如果你的应用程序中必须有同一类型对象的不同实例,你就不能使用它(例如,如果你希望只在你的应用程序的一半中模拟一个类) 当然,拥有所有的选择真的很棒。您可以使用getter/setter、构造函数,当省略初始化时,默认值取自单例工厂。但是在不需要的时候有太多的选项并不可怕,这是令人不安的,因为程序员必须弄清楚使用哪种约定、选项和模式。我绝对不想仅仅为了运行一个简单的CRUD而做出几十个设计决策 如果你看看其他框架,你会发现没有银弹。通常,单个框架根据上下文使用不同的技术。在控制器中,DI是一件非常简单的事情,看看CakePHP的$helpers,$components变量,它指示将适当的变量注入控制器类。对于应用程序本身来说,单例仍然是一件好事,因为总是只有一个单例 // 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); }
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'));
}
}
}
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();
}
}
}