优雅应对代码上下文不同,功能相同的情况

发布时间:2024年01月05日

欢迎关注微信公众号: 线程
在这里插入图片描述

在我们写代码中经常碰到这样一种情况,那就是 在不同的上下文中,去处理同一个功能。例如:

  1. 在A的情境下,我们可能希望该操作引发一个异常。
  2. 而在B的情境中,我们可能期望该操作返回一个空值。

比如现在一个项目中, 如果要去解决这样两个 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),

文章来源:https://blog.csdn.net/user_03/article/details/133849545
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。