Многие строят одну реализацию. Я безнадежен? Использовать сервисный локатор?

14

Скажем, у нас есть 1001 клиент, который строит свои зависимости напрямую, а не принимает инъекции. Рефакторинг 1001 не вариант по мнению нашего босса. На самом деле нам даже не разрешен доступ к их источнику, только к файлам классов.

Мы должны «модернизировать» систему, через которую проходят эти 1001 клиент. Мы можем рефакторинг, что все, что нам нравится. Зависимости являются частью этой системы. И некоторые из этих зависимостей мы должны изменить, чтобы иметь новую реализацию.

Мы хотели бы иметь возможность настраивать различные реализации зависимостей для удовлетворения этого множества клиентов. К сожалению, DI не представляется возможным, так как клиенты не принимают инъекции с конструкторами или сеттерами.

Варианты:

1) Измените реализацию службы, которую используют клиенты, чтобы она выполняла то, что нужно клиентам сейчас. Взрыв, мы сделали. Не гибкий Не сложный

2) Рефакторизовать реализацию так, чтобы она делегировала свою работу еще одной зависимости, которую она приобретает через фабрику. Теперь мы можем контролировать, какую реализацию они все используют, путем рефакторинга фабрики.

3) Рефакторизовать реализацию так, чтобы она делегировала свою работу еще одной зависимости, которую она получает через локатор службы. Теперь мы можем контролировать, какую реализацию они все используют, настроив локатор службы, который может быть простоhashmap строкой для объектов с небольшим продолжением приведения.

4) Что-то, о чем я даже не думал.

Цель:

Минимизируйте ущерб, нанесенный дизайну, перетаскивая старый плохо спроектированный клиентский код в будущее, не добавляя при этом лишней сложности.

Клиенты не должны знать или контролировать реализацию своих зависимостей, но они настаивают на их построении new. Мы не можем контролировать, newно мы контролируем класс, который они строят.

Мой вопрос:

Что я не учел?


Вопросы от Дока Брауна

вам действительно нужна возможность настройки между различными реализациями? С какой целью?

Ловкость. Много неизвестных. Менеджмент хочет потенциала для изменений. Только потерять зависимость от внешнего мира. Также тестирование.

Вам нужна механика времени выполнения или просто механика времени компиляции для переключения между различными реализациями? Почему?

Механика времени компиляции вполне вероятна. За исключением тестирования.

какая гранулярность вам нужна для переключения между реализациями? Все вместе? За модуль (каждый из которых содержит группу классов)? В классе?

Из 1001 только один запускается через систему одновременно. Изменение того, что все клиенты используют одновременно, вполне вероятно. Индивидуальный контроль над зависимостями, вероятно, важен, хотя.

кому нужно управлять выключателем? Только ваша / ваша команда разработчиков? Администратор? Каждый клиент по-своему? Или разработчики поддержки для кода клиента? Итак, насколько простой / надежной / надежной должна быть механика?

Dev для тестирования. Администратор как внешние аппаратные зависимости меняются. Это должно быть легко проверить и настроить.


Наша цель - показать, что система может быть быстро переделана и модернизирована.


Фактический вариант использования для реализации переключения?

Во-первых, некоторые данные будут предоставляться программным обеспечением до тех пор, пока аппаратное решение не будет готово.

candied_orange
источник
1
Я не чувствую, что вы многого добьетесь, храня фабрики или локаторы в глобальном состоянии. Зачем беспокоиться? Собираетесь ли вы тестировать клиентов, заменяющих зависимость?
Basilevs
Рассмотрите возможность использования пользовательского загрузчика классов.
Basilevs
Просто из любопытства, что делает эта система?
Прайм

Ответы:

7

Ну, я не уверен, что полностью понимаю технические детали и их точные отличия от поддерживаемых вами решений, но ИМХО вам сначала нужно выяснить, какая гибкость вам действительно нужна.

Вопросы, которые вы должны задать себе:

  • вам действительно нужна возможность настройки между различными реализациями? С какой целью?

  • Вам нужна механика времени выполнения или просто механика времени компиляции для переключения между различными реализациями? Почему?

  • какая гранулярность вам нужна для переключения между реализациями? Все вместе? За модуль (каждый из которых содержит группу классов)? В классе?

  • кому нужно управлять выключателем? Только ваша / ваша команда разработчиков? Администратор? Каждый клиент по-своему? Или разработчики поддержки для кода клиента? Итак, насколько простой / надежной / надежной должна быть механика?

Имея в виду ответы на эти вопросы, выберите самое простое решение, которое вы можете придумать, которое обеспечивает необходимую гибкость. Не используйте гибкость, в которой вы не уверены, «на всякий случай», если это требует дополнительных усилий.

В ответ на ваши ответы: если у вас есть хотя бы один реальный вариант использования, используйте его для проверки ваших проектных решений. Используйте этот вариант, чтобы узнать, какое решение лучше всего подходит для вас. Просто попробуйте, если «фабрика» или «сервисный локатор» предоставит вам то, что вам нужно, или если вам нужно что-то еще. Если вы считаете, что оба решения одинаково хороши для вашего случая, бросьте кубик.

Док Браун
источник
Хорошие вопросы! Пожалуйста, смотрите обновление.
candied_orange
Обновлено с фактическим вариантом использования.
candied_orange
@CandiedOrange: Я не могу дать вам немного рыбы, я могу только попытаться помочь вам ловить рыбу самостоятельно :-)
Док Браун
Понял. Я просто смотрю на воду, размышляя, нужна ли мне лучшая приманка или другая рыбалка. Я хотел бы сделать правильный DI, но эта ситуация, кажется, не позволяет этого.
candied_orange
1
@CandiedOrange Не зацикливайтесь на желании сделать DI, потому что это «хорошо» или «лучше», чем все остальное. DI является одним из конкретных решений проблемы (или иногда несколько). Но это не всегда самое подходящее решение. Не влюбляйся в него ... относись к нему так, как есть. Это инструмент.
svidgen
2

Просто чтобы убедиться, что я правильно понял. Например, у вас есть сервис, состоящий из нескольких классов C1,...,Cnи группы клиентов, которые напрямую звонят new Ci(...). Поэтому я думаю, что согласен с общей структурой вашего решения, которая заключается в создании новой внутренней службы с некоторыми новыми классами D1,...,Dn, которые хороши и современны, и внедряют их зависимости (разрешают такие зависимости через стек), а затем переписывают, Ciчтобы ничего не делать, кроме как создавать и Delgate to Di. Вопрос в том, как это сделать, и вы предложили несколько способов 2и 3.

Чтобы дать аналогичное предложение 2. Вы можете использовать внедрение зависимостей как Diвнутри, так и внутри, а затем создать нормальный составной корень R(используя каркас, если считаете это необходимым), который отвечает за сборку графа объектов. Пихайте Rза статичный завод и дайте каждому Ciпройти Diчерез это. Так, например, вы могли бы иметь

public class OldClassI {
    private final NewClassI newImplementation;

    public OldClassI(Object parameter) {
        this.newImplementation = CompositionRoot.getInstance().getNewClassI(parameter);
    }
}

По сути, это ваше решение 2, но оно собирает все ваши фабрики в одном месте вместе с остальной частью внедрения зависимости.

walpen
источник
Я согласен с этим. Только не уверен, что это не то же самое, что указатель службы из варианта 3. Ожидаете ли вы создавать новый экземпляр с каждым вызовом getInstance()?
candied_orange
@CandiedOrange Это, вероятно, просто конфликт в использовании, для меня локатор службы - это, в частности, просто хэш-карта от типов к объектам, тогда как корень композиции - это просто объект с набором методов, которые конструируют другие объекты.
Walpen
1

Начните с простых, ничего необычных реализаций.

Если вам позже потребуется создать дополнительные реализации, вопрос в том, когда будет принято решение о реализации.

Если вам нужна гибкость «времени установки» (каждая установка клиента использует одну статическую реализацию), вам все равно не нужно делать что-то необычное. Вы просто предлагаете разные DLL или SO (или любой другой формат вашей библиотеки). Клиенту просто нужно положить правильный в libпапку ...

Если вам нужна гибкость во время выполнения, вам просто понадобится тонкий адаптер и механизм выбора реализации. Используете ли вы контейнер фабрики, локатора или IoC, в основном спорный вопрос. Единственными существенными различиями между адаптером и локатором являются A) Именование и B) Является ли возвращаемый объект одноэлементным или выделенным экземпляром. И большая разница между контейнером IoC и фабрикой / локатором заключается в том, кто кого называет . (Часто это вопрос личных предпочтений.)

svidgen
источник