Я создаю небольшой проект, чтобы попытаться научить себя как можно большей части основ, что для меня означает не использовать сборную фреймворк (как Jeff как-то положил его, "Не изобретайте колесо, , если вы не планируете больше узнать о колесах" [основное внимание]) и следуя принципам разработки, управляемой испытаниями.
В моих поисках я недавно столкнулся с концепцией 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? Любые критические замечания и предложения приветствуются.