技术开发 频道

在方法签名中使用控制反转(IoC)

  工厂方法

  您可能想了解图 3 中的 OrderDB 组件及其 readCart() 方法。确实,这是一个特殊情况。在前面的例子中,依赖项方法(如 getPrices(...))修改过的所有对象均已作为方法参数进行传递。当组件正在从数据库中读取数据时这是不可能的,因为在本例中,购物车中的条目数量在读取前是未知的。

  这里的解决方案是为要读取的条目提供一个带工厂方法的方法参数,如清单 2 所示:

  清单 2:在方法参数中使用工厂方法

1 interface OrderDBComponent {
2
3   void readCart(Cart cart) throws OrderDBException;
4
5   }
6
7   interface Cart {
8
9   Long getCartId();
10
11   CartItem newItem();
12
13   void addItem(CartItem item);
14
15   }
16
17   interface CartItem {
18
19   void setArticleId(Long articleId);
20
21   ...
22
23   }
24
25

  借助这个定义,OrderDB 组件从数据库中读取条目,并且对于读取的每个条目,都使用 newItem() 方法从 Cart 对象中获取新的条目对象(CartItem)。填充了从数据库中读取的值后,通过 addItem() 方法把 CartItem 添加到购物车中。请注意,向条目填充值后再将该条目添加到购物车中,能够使购物车总是保持一致。

  接口和方法不匹配

  这个方法适合以下情况,即多个依赖项组件定义的参数接口是兼容的,从而能够由相同的对象实现。在某些情况下可能出现不兼容,例如两个接口使用不同的返回值类型定义同一个方法。必须小心设计这些方法,才能不引入这类不兼容情况。同样,在设计这些方法时,还应确保当不同的接口有相同的方法签名时,方法参数接口所定义的方法具有相同的语义。

  然而即使存在不兼容,所有数据也并未丢失!适配器对象能够将对象转换为实现所需接口的对象,而不必复制涉及到的数据。尽管必须使用该方式来实例化适配器对象,但这还是避免了复制数据值。

  另一个例子

  还有一个例子能显示此方法的灵活性。我编写了一个针对特定对象模型的编辑器,但希望将模型实现和编辑器实现分离开来。因此,让编辑器为可以编辑的模型定义接口。然后由真实的模型实现来实现清单 3 中的接口:

  清单 3. 模型编辑器接口样例

1 interface ModelEditor {
2
3   void edit(Model model);
4
5   }
6
7   interface Model {
8
9   ModelElement newElement();
10
11   ModelElement addElement(ModelElement element);
12
13   }
14
15

  在这个(相当)精简的定义中,可以看到模型的 addElement() 方法不仅把 ModelElement 当作参数,还返回一个 ModelElement 实例。返回的 ModelElement 是新添加的模型元素所替换的模型元素,如果没有替换任何元素,则为 NULL。然后,将返回值存储到一个撤销命令中,这样就能通过再次调用 addElement() 轻松地恢复该模型。同样,addElement() 方法实现模型一致性检验并拒绝无效更改。

  结束语

  本文展示了 IoC 的一种具体形式,即应用于组件方法的参数而非组件。使用接口作为方法参数是上下文 IoC 的一种形式(这是 IoC 术语),应用于调用程序的依赖项。就像将依赖项组件(如 PriceComponent)注入到调用程序的组件(如 OrderPrepareComponent)中一样,调用程序组件也将其依赖项对象(方法参数接口的实现)注入到依赖项组件的方法中。由于被调用的组件仅限于在参数接口中定义的方法,所以接口能够确保作为参数提供的对象是一致的。小心地减少功能上所必需的方法的接口,就会降低组件之间的耦合。

0
相关文章