Предупреждение : следующий пост содержит использование антипаттерна Singleton в качестве временного инструмента для рефакторинга крупномасштабного кластерного фука
Я думаю, что вы, возможно, пропустили важную часть рефакторинга (или, по крайней мере, сосредоточились на неправильной части). Вы должны стремиться как конкретный конечный результат в этом процессе. Конкретная архитектурная структура, которую вы хотите увидеть (и с которой согласны все ваши коллеги). Прямо сейчас вы, кажется, просто бесцельно пытаетесь "сделать это менее дерьмовым" .
В начале ...
Ваша ближайшая веха - "избавиться от глобальных переменных" . Итак, сосредоточимся на этом. Попытка одновременно переключиться на полностью внедренную структуру зависимости приведет к путанице и увеличит вероятность неудачи.
Вместо этого вам следует начать с переключения в реестр (вы можете использовать синглтоны в качестве временного шага, но потом их сложнее очистить).
Итак, код, который выглядел так:
class Foobar {
function something() {
global $database;
$database->query('SELECT ... blah');
}
}
Будет переписано как:
class Foobar {
function something() {
$database = Registry::getDatabase();
$database->query('SELECT ... blah');
}
}
Это решило бы одну конкретную проблему: избавление от глобальных переменных.
Второй шаг
Когда это будет сделано, вы можете приступить к переключению на инъекции зависимостей. Но не путем добавления шрифтов. Это потребовало бы огромных переписываний с большой «ударной поверхностью» (что-то легко сломать).
Вместо этого вы начинаете вводить контейнер DI, но только для частей, которые ранее использовали синглтоны или реестр. Вы создаете синглтон, который содержит экземпляр контейнера DI (вы в основном используете его как локатор службы antipattern), и код переписывается как:
class Foobar {
function something() {
$database = Locator::get('database');
$database->query('SELECT ... blah');
}
}
Вот как можно реорганизовать процедурный код (который также включает классы только для статики) в этом направлении.
Третий шаг
Затем вам нужно начать выполнять ООП. Вам нужно, чтобы эти (ранее глобальные) переменные были перемещены в общий доступ с экземпляром класса:
class Foobar {
private $database;
public function __construct() {
$this->database = Locator::get('database');
}
public function something() {
$this->database->query('SELECT ... blah');
}
}
Четвертый шаг
И только теперь вы можете перейти к использованию фактических зависимостей в конструкторах.
class Foobar {
private $database;
public function __construct(Database $database) {
$this->database = $database;
}
public function something() {
$this->database->query('SELECT ... blah');
}
}
Вам также необходимо заменить каждый случай, где у вас есть: $thing = new Foobar();
на
$thing = Locator::get('foobar');
, а затем повторите шаг 3 для класса, в котором использовался Foobar
, до достижения начальной загрузки.
Конец цикла
Когда вы завершите процесс миграции, вы перестанете использовать одноэлементную оболочку Locator
в файле начальной загрузки и переключитесь на использование фактического экземпляра контейнера DI.
Вы могли также заметить, что каждый из шагов может быть выполнен, в то время как новые функции также добавляются в базу кода, потому что ни в коем случае не требуется касаться больших участков кода в несвязанном месте. Миграция каждого класса может быть выполнена отдельно, не нарушая функциональность приложения в целом.
Но
(а это большое но).
Из фрагмента кода, который вы показали, у вас будет другая проблема, которую вам нужно решить. Весьма вероятно, что ваш класс User
на самом деле реализует активную запись antipattern. Итак, когда вы избавитесь от глобальных переменных, одним из следующих возможных шагов будет отделение логики постоянства от бизнес-логики и переключение на шаблон data mapper . Для дальнейшего прочтения я спамлю три моих старых сообщения: 1 , 2 и 3 .