我正在建立一个小项目,尽量教自己尽可能多的基础知识,对我而言,意味着不要使用预制框架(As Jeff
once put it,“不要重塑轮子,除非你计划学习更多关于轮子“[重点]),并遵循测试驱动发展的原则。
在我的追求中,我最近遇到了Dependency Injection的概念,这对TDD很重要。我的问题是我不能把我的头围绕着它。到目前为止,我的理解是,它或多或少相当于“让呼叫者通过类/方法可能需要的任何其他类,而不是让他们自己创建”。
我有两个我想用DI解决的示例问题。我在这些重构的正确轨道上吗?
数据库连接
我打算使用单例来处理数据库,因为我目前不希望使用多个数据库。最初,我的模型看起来像这样:
class Post {
private $id;
private $body;
public static function getPostById($id) {
$db = Database::getDB();
$db->query("SELECT...");
//etc.
return new Post($id, $body);
}
public function edit($newBody) {
$db = Database::getDB();
$db->query("UPDATE...");
//etc.
}
}
用DI,我觉得看起来更像这样:
class Post {
private $db; // new member
private $id;
private $body;
public static function getPostById($id, $db) { // new parameter
$db->query("SELECT..."); // uses parameter
//etc.
return new Post($db, $id, $body);
}
public function edit($id, $newBody) {
$this->db->query("UPDATE..."); // uses member
//etc.
}
}
我仍然可以使用单例,使用应用程序设置中指定的凭据,但是我只需要从控制器传递它(控制器无论如何都是无法测试的):
Post::getPostById(123, Database::getDB);
模型调用模型
举个例子,一个有观点的帖子。由于确定视图是否为新的逻辑不是Post对象特有的,所以它只是在自己的对象上成为静态方法。然后Post对象就会调用它:
class Post {
//...
public function addView() {
if (PageView::registerView("post", $this->id) {
$db = Database::getDB();
$db->query("UPDATE..");
$this->viewCount++;
}
}
用DI,我觉得看起来更像这样:
class Post {
private $db;
//...
public function addView($viewRegistry) {
if ($viewRegistry->registerView("post", $this->id, $this->db) {
$this->db->query("UPDATE..");
$this->viewCount++;
}
}
这将调用从控制器更改为:
$post->addView(new PageView());
这意味着实例化一个只有静态方法的类的新实例,这对我来说很不好(我认为在某些语言中是不可能的,但是在这里是可行的,因为PHP不允许类本身是静态的)。
在这种情况下,我们只能深入一层,所以使控制器实例化一切似乎都可行(尽管PageView类通过Post的成员变量间接获取了数据库连接),但是如果你觉得不得不调用一个需要一个需要一个类的类的类的方法。我想这只是意味着这也是一个代码气味。
我正在跟踪这个轨道,还是完全误解了DI?任何批评和建议都不胜感激。