Как предоставить модель плагина, в которой разные плагины принимают разные параметры - PullRequest
4 голосов
/ 06 апреля 2011

Код, который я пишу (MyService), включает возможность для каждого клиента подключать свой собственный калькулятор в определенный момент обработки. Это позволяет настраивать бизнес-правила. В момент расчета мы знаем множество вещей, некоторые из которых могут иметь отношение к расчету. MyService будет создано для определенного набора входных параметров и будет запущено один раз.

Мой план состоит в том, чтобы использовать внедрение зависимостей, чтобы дать MyService a calculator в конструкторе. Это позволяет различным пользователям подключать свои собственные calculator. calculator вернет сумму, представляющую надбавку, подлежащую уплате за этот конкретный прогон MyService. Другие будут внедрять различные калькуляторы, и мне нужно будет обновить мой код, не нарушая их. то есть. сохранить обратную совместимость.

Проблема, с которой я столкнулся, заключается в том, что для различных реализаций калькулятора требуются разные параметры. Эти параметры не могут быть введены конструктором в калькулятор во время создания MyService, поскольку они не известны, происходит некоторая обработка в MyService.

Калькулятор будет вызываться только один раз в конкретном экземпляре MyService. Поэтому в одном крайнем случае все параметры могут быть переданы в конструктор, и есть метод без параметров, который возвращает ответ. С другой стороны, все параметры передаются в вызове метода.

AlwaysZeroCalculator может просто return 0, поэтому параметры не требуются. PercentageCalculator необходимо amount, чтобы применить процент. Еще более сложный нужен amount и customerNumber. Мы можем предположить, что все, что может понадобиться calculator, известно MyService во время выполнения (или само может быть внедрено в реализацию calculator, например, в Hibernate Session).

Как я могу это реализовать?

Вот несколько вариантов и проблем:

  • Заставить все калькуляторы реализовать интерфейс, который включает все параметры в качестве аргументов метода. Но если что-то добавить, то все они должны измениться, что неизбежно превратит это во второй вариант.
  • Создание различных интерфейсов (ICalculator, ICalculatorWithAmount, ICalculatorWithAmountAndCustomerNumber и т. Д.). MyService нужно будет увидеть, какой интерфейс реализует calculator, который он реализует, привести его к этому интерфейсу, а затем вызвать соответствующий метод calculate(..).
  • Введите объект параметра, который включает в себя все, что волнует любого из них. Это означает, что даже самый простой calculator зависит от всего.
  • Создайте разные интерфейсы и разные версии MyService, которые ожидают один из этих интерфейсов.
  • Введите calculatorFactory вместо calculator. Фабрика примет все возможные параметры и создаст калькулятор только с правильными. Это, кажется, просто перемещает проблему куда-то еще, не решая ее.
  • Передайте какой-нибудь ужасный хэш-карту калькулятору и введите безопасность, будьте прокляты декларации зависимостей

Есть ли лучший способ?

Ответы [ 4 ]

2 голосов
/ 06 апреля 2011

Это две проблемы:

  • с помощью калькулятора
  • создание калькулятора

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

Из того, что вы перечислили, наилучшим решением является завод.И это не перемещает проблему в другое место, это решает проблему.Каждая фабрика знает свой калькулятор и знает, какие параметры ему нужны.Так что завод зависит от калькулятора.Создание калькулятора с использованием фабрики создает зависимость только от создания калькулятора, а не от его параметров или самого калькулятора. Он может быть частью интерфейса фабрики, который реализуют все фабрики.

Предоставление параметров для фабрики может бытьрешается путем внедрения свойств с использованием любых средств, которые вы выберете - Spring определенно неплох в этом.

Приложение, в свою очередь, знает только, как использовать калькулятор, поэтому зависит от общего интерфейса калькулятора, а не от конкретной реализации калькулятора.

1 голос
/ 03 сентября 2015

Я всегда изо всех сил стараюсь, чтобы зависимости времени выполнения работали с Dependency Injection элегантным способом.

Однако я бы хотел изменить свой план «Создание разных интерфейсов».

Каждыйкалькулятор определит свой собственный интерфейс, скажем IAlwaysZeroCalculatorParameters, IPercentageCalculatorParameters и т. д.

Ваш класс MyService может затем реализовать все эти интерфейсы и затем передать себя каждому калькулятору.

Для достижения этой цели очень мало дополнительных затрат, и это означает, что каждый калькулятор выражает кратко то, что ему нужно.Это работает очень хорошо для меня в подобных ситуациях, хотя в вашем конкретном случае (когда вы не можете контролировать плагины), это означает, что вы должны выпускать MyService каждый раз, когда кто-то внедряет новый калькулятор (чтобы он мог реализоватьновый интерфейс).Если понадобится, я бы, возможно, обошел это с помощью Duck Typing.

1 голос
/ 06 апреля 2011

Предполагается, что вы используете пружину ... Возможно, атрибуты factory-bean и factory-method тега bean могут быть полезны.

Псевдокод ...

<bean id="calculator1"
 factory-bean="calculatorFactory"
     factory-method="getAlwaysZeroCalculator">
         <!--AlwaysZeroCalculator args go here --> 
</bean>
<bean id="calculator2"
 factory-bean="calculatorFactory"
     factory-method="getPercentageCalculator ">
         <!--PercentageCalculator args go here --> 
</bean>
0 голосов
/ 06 апреля 2011

Этот калькулятор использует abstract FunctionAdapter для оценки функций по имени. Каждый конкретный подкласс реализует метод eval(), который принимает переменное число аргументов.

...