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