如何提供插件模型,其中不同的插件采用不同的参数

时间:2011-04-06 05:56:40

标签: java plugins interface dependency-injection

我正在编写的代码(MyService)包括每个客户在处理过程中的特定点插入自己的计算器的能力。这是为了允许自定义业务规则。在计算完成时,我们知道各种各样的事情,其中​​一些可能与计算有关。将为特定的输入参数集创建MyService并运行一次。

我的计划是使用依赖注入在构造函数中为MyService calculator提供一个calculator。这允许不同的用户插入他们自己的calculatorMyService将返回代表此特定MyService运行应付附加费的金额。其他人将实现各种计算器,我需要能够更新我的代码而不会破坏他们的代码。即。保持向后兼容性。

我遇到的问题是各种计算器实现需要不同的参数。这些参数在创建MyService时无法将构造函数注入计算器,因为不知道MyService中的某些处理发生。

计算器只会在AlwaysZeroCalculator的特定实例中调用一次。因此,在一个极端,所有参数都可以在构造函数中传递,并且有一个方法没有返回答案的参数。另一方面,所有参数都在方法调用中传递。

return 0可能只是PercentageCalculator所以不需要参数。 amount需要amount才能应用百分比。一个更复杂的需要customerNumbercalculator。我们可以假设MyService在运行时可能知道calculator可能需要的任何内容(或者它本身可以注入到ICalculator实现中,就像hibernate Session一样。)

我该如何实现?

以下是一些选项和问题:

  • 让所有计算器实现一个包含所有参数作为方法参数的接口。但是,如果添加额外的东西,那么他们都需要改变,这将不可避免地将其转变为第二种选择。
  • 制作不同的界面(ICalculatorWithAmountICalculatorWithAmountAndCustomerNumberMyService等)。 calculator需要查看它实现的calculate(..)接口,将其强制转换为该接口,然后调用相应的calculator方法。
  • 介绍一个参数对象,其中包含任何他们关心的任何内容。这意味着即使是最简单的MyService也取决于所有内容。
  • 制作不同的接口和calculatorFactory的不同版本,以期望其中一个接口。
  • 注入calculator而不是{{1}}。工厂将采用所有可能的参数,并创建一个只有正确的计算器。这似乎只是将问题转移到其他地方而没有解决它。
  • 将一些可怕的hashmap传递给计算器并键入安全性,依赖性声明被诅咒

有更好的方法吗?

4 个答案:

答案 0 :(得分:2)

这是两个问题:

  • 使用计算器
  • 实例化计算器

第一个很容易 - 所有计算器都做同样的事情,可以有相同的界面。 后者是要解决的问题,因为它需要每个计算器实例的不同参数。

从您列出的是工厂最佳解决方案。它并没有将问题转移到其他地方,它解决了这个问题。每个工厂都知道它的计算器并知道它需要什么参数。所以工厂依靠计算器。使用工厂创建计算器只会创建计算器而不是参数或计算器本身,它可以是所有工厂实现的工厂界面的一部分。

为工厂提供参数可以通过使用您选择的任何方式进行物业注入来解决 - 春天绝对不错。

应用程序反过来只知道如何使用计算器,因此取决于通用计算器接口而不是任何特定的计算器实现。

答案 1 :(得分:1)

假设你正在使用春天...... 也许bean标记的factory-bean和factory-method属性可能很有用。

伪代码......

<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>

答案 2 :(得分:1)

我总是很难以优雅的方式获得运行时依赖项以使用依赖注入。

但是,我会针对您的“制作不同界面”计划进行修改。

每个计算器都会定义它自己的界面,比如IAlwaysZeroCalculatorParametersIPercentageCalculatorParameters等。

然后,您的MyService类可以实现所有这些接口,然后将其自身传递给每个计算器。

实现这一目标的框架开销很少,这意味着每个计算器都能简洁地表达它所需要的内容。在类似情况下,它对我来说非常有效,尽管在您的特定情况下(您无法控制插件),这意味着每次有人实施新计算器时您必须释放MyService(以便它可以实现新的界面)。如果需要,我可能会用一些Duck Typing来解决这个问题。

答案 3 :(得分:0)

calculator使用abstract FunctionAdapter按名称评估功能。每个具体的子类实现一个eval()方法,该方法接受可变数量的参数。