欢迎关注微信公众号: 线程
在我们写代码中经常碰到这样一种情况,那就是 在不同的上下文中,去处理同一个功能。例如:
比如现在一个项目中, 如果要去解决这样两个 path。
间接调用 get 方法的时候, 如果依赖无法找到, 我们希望抛出一个异常
首先我们先写一个测试, 表示我们将要完成的目标
@Test
public void should_throw_exception_if_dependency_not_found() {
Conext context;
Conext.bind(Component.class, ComponentWithInjectConstructor.class);
assertThrows(DependencyNotFoundException.class, () -> context.get(component.class));
}
然后对应到代码的修改, 是这样的:
//provider是一个接口, 包含一个方法get
Map<Class<?>, Provider<?>> providers = new HashMap<>();
public <Type> Type get(Class<Type> type) {
//如果注入的map没有对应的依赖, 那么就会抛出异常
if (!providers.containsKey(type)) throw new DependencyNotFound();
providers.get(type).get();
}
一旦实现了第一个目标,我们就需要着手迎接第二个目标, 第二个目标是直接调用
直接调用 get 的时候, 依赖无法找到
Conext context = new Context();
//直接去取, 依赖不存在,是返回异常还是另外一种处理
context.get(Component.class);
你会发现直接调用 get 我们的第一个情形存在重叠,即它们都依赖于无法找到的情况。在这种情况下,我们是继续返回异常,还是采取另一种行为呢?
显然,在第一个上下文中调用get时,我们希望它返回一个异常,而在第二种直接调用get的情况下,我们希望返回空值。
有趣的是,我们需要修改的代码与第一次修改的是同一段,即 get 方法。然而,如果直接修改 get 方法并返回空值,我们会发现我们之前对第一个点的处理不够理想。因为无论是直接调用还是间接调用,第一个点都会抛出异常,就无法返回空值了。
碰到这种冲突的情况可能很多人就会束手无策了, 但是我们可以使用重构的手法来完成它, 首先可以新创建一个方法 get_, 然后在新的方法中完成我们的功能, 最后在内联。我们看代码:
先写一段测试,
@Test
public should_return_empty_if_component_not_defined() {
//设置一个新的方法, 因为之前对get进行修改了,
Optional<Component> component = conext.get_(Component.class);
assertTrue(component.isEmpty());
}
然后在Conext中创建一个 get_ 方法, 因为 get 代码无论如何都抛异常。
public <Type> Optional<Type> get_(Class<Type> type) {
return Optional.ofNullable(providers.get(type)).map(provider -> (Type) provider.get());
}
那么对get的修改就是:
public <Type> Type get(Class<Type> type) {
get_().orElseThrow(DependencyNotFound::new);
}
然后跑一下测试, 将 get 方法内联(inline),