PHP依赖注入和松耦合
我正在考虑几种不同的方法,非常感谢您的意见!我正在考虑下面的两个选择。我有两个问题要问PHP依赖注入和松耦合,php,design-patterns,interface,dependency-injection,Php,Design Patterns,Interface,Dependency Injection,我正在考虑几种不同的方法,非常感谢您的意见!我正在考虑下面的两个选择。我有两个问题要问 首选将依赖项注入主“容器”类的构造函数中,还是在容器类内创建新实例 在第二个示例中,通过构造函数注入类的依赖项,然后通过类的属性在其中进行维护。然后,当调用方法(route()、render())时,将从内部调用依赖项。我开始采用这种方法,但现在更倾向于采用与第一个示例类似的方法。我认为第一个例子更可取,但是在第二个例子中使用DI方法有什么好处吗 实际上不需要将类中的任何内容存储为属性。我可能可以重新安排一切
class App
{
private $config;
private $router;
private $renderer;
public function __construct(IConfig $config, IRouter $router, IRenderer $renderer)
{
$this->config = $config;
$this->router = $router;
$this->renderer = $renderer;
$this->run();
}
public function run()
{
$data = $this->router->route(new Request, $config->routes);
$this->renderer->render($data);
}
}
class App
{
private $config;
private $router;
private $renderer;
public function __construct()
{
$this->config = new Config;
$this->run();
}
public function run()
{
$this->router = new Router(new Request, $config->routes);
$this->router->route();
$this->renderer = new Renderer($this->router->getData());
$this->renderer->render();
}
}
最好将依赖项注入构造函数中 在构造函数中创建实例会在两个类之间创建紧密耦合。使用具有清晰签名的构造函数,如
public function __construct(IConfig $config, IRouter $router, IRenderer $renderer)
我可以立即知道这个组件需要做什么
给定一个类似
public function __construct();
目前还不知道该组件需要什么功能。它与每个路由器、请求和渲染器的特定实现产生了强烈的耦合,在深入挖掘类的本质之前,所有这些都不明显
总而言之,第一种方法是文档化的、可扩展的和可测试的。
第二种方法不透明,高度耦合,不易测试。虽然Orangepill提出了一个很好的观点,但我想我也应该插手。我也倾向于用一个清晰的构造函数来定义我的构造函数,但我不希望在创建实例时传递所需的对象
有时,您会创建一个实例,从DB或某种Http请求检索数据。在您的例子中,第一个示例期望传递三个依赖项,但是谁能说您总是需要这三个依赖项呢 输入延迟加载。下面的代码示例相当长,但(IMO)非常值得研究。如果我使用服务,我不想加载所有依赖项,除非我确定我会使用它们。这就是我定义构造函数的原因,这样我可以通过以下任一方式创建实例:
$foo = new MyService($configObj);
$bar = new MyService($configObj, null, $dbObj);//don't load curl (yet)
$baz = new MyService($configObj, $curlObj);//don't load db (yet)
如果我想运行一些测试,我仍然可以在构建实例时注入依赖项,或者我可以依赖一个测试配置对象,或者我也可以使用setDb
和setCurl
方法:
$foo->setCurl($testCurl);
坚持第一种构造实例的方法,我可以有把握地说,如果我只调用getViaCurl
方法,Db
类将永远不会被加载getViaDb
方法有点复杂(就像getDb
方法一样)。我不建议您使用这样的方法,但这只是为了向您展示这种方法的灵活性。我可以将参数数组传递给getViaDb
方法,该方法可以包含自定义连接。我还可以传递一个布尔值来控制我对该连接所做的操作(仅在这一次调用中使用它,或者将该连接分配给MyService
实例)
我希望这不太清楚,但我很累,所以我不太擅长解释这些东西。这里的代码,无论如何…它应该是相当自我解释
class MyService
{
private $curl = null;
private $db = null;
private $conf = null;
public function __construct(Config $configObj, Curl $curlObj = null, Db $dbObj = null)
{
$this->conf = $configObj;//you'll see why I do need this in a minute
$this->curl = $curlObj;//might be null
$this->db = $dbObj;
}
public function getViaCurl(Something $useful)
{
$curl = $this->getCurl();//<-- this is where the magic happens
return $curl->request($useful);
}
public function getViaDb(array $params)
{
if (isset($params['custom']))
{
$db = $this->getDb($params['custom'], $params['switch']);
}
else
{//default
$db = $this->getDb();
}
return $db->query($params['request']);
}
public function getCurl()
{//return current Curl, or load default if none set
if ($this->curl === null)
{//fallback to default from $this->conf
$this->curl = new Curl($this->conf->getSection('CurlConf'));
}
return $this->curl;
}
public function setCurl(Curl $curlObj)
{//inject after instance is created here
if ($this->curl instanceof Curl)
{//close current connection
$this->curl->close();
}
$this->curl = $curlObj;
}
public function setDb(Db $dbObj)
{
if ($this->db instanceof Db)
{//commit & close
$this->db->commit();
$this->db->close();
}
$this->db = $dbObj;
}
//more elaborate, even:
public function getDb(Db $custom = null, $switch = false)
{
if ($custom && !!$swith === true)
{
$this->setDb($custom);
return $this->db;
}
if ($custom)
{//use custom Db, only this one time
return $custom;
}
if ($this->db === null)
{
$this->db = new Db($this->conf->getSection('Db'));
}
return $this->db;
}
}
classmyservice
{
private$curl=null;
私有$db=null;
private$conf=null;
公共函数构造(配置$configObj,Curl$curlObj=null,Db$dbObj=null)
{
$this->conf=$configObj;//您马上就会明白我为什么需要这个了
$this->curl=$curlObj;//可能为空
$this->db=$dbObj;
}
公共函数getViaCurl(一些$usiver)
{
$curl=$this->getCurl();//请求($use);
}
公共函数getViaDb(数组$params)
{
如果(isset($params['custom']))
{
$db=$this->getDb($params['custom'],$params['switch']);
}
其他的
{//默认值
$db=$this->getDb();
}
返回$db->query($params['request']);
}
公共函数getCurl()
{//返回当前旋度,如果未设置,则加载默认值
如果($this->curl==null)
{//从$this->conf返回默认值
$this->curl=newcurl($this->conf->getSection('CurlConf');
}
返回$this->curl;
}
公共函数setCurl(Curl$curlObj)
{//在此处创建实例后注入
if($this->curl instanceof curl)
{//关闭当前连接
$this->curl->close();
}
$this->curl=$curlObj;
}
公共函数setDb(Db$dbObj)
{
if($this->db instanceof db)
{//提交并关闭
$this->db->commit();
$this->db->close();
}
$this->db=$dbObj;
}
//更详细的,甚至是:
公共函数getDb(Db$custom=null,$switch=false)
{
如果($custom&&!!$swith===true)
{
$this->setDb($custom);
返回$this->db;
}
如果($custom)
{//使用自定义数据库,仅此一次
返回$custom;
}
如果($this->db==null)
{
$this->db=newdb($this->conf->getSection('db');
}
返回$this->db;
}
}
我知道这是一个古老的回答,但是,实现cUrl和DB的接口不是这样吗?