小编典典

编写/实现API:可测试性与信息隐藏

java

我很多时候都参与API的设计/实现,我面临着这个难题。

我是信息隐藏的坚决支持者,并为此尝试使用各种技术,包括但不限于内部类,私有方法,程序包私有限定符等。

这些技术的问题在于它们倾向于防止良好的可测试性。尽管其中一些技术可以解决(例如,通过将一个类放入同一程序包来实现程序包私有性),但其他技术却不那么容易解决,并且需要反射魔术或其他技巧。

让我们看一个具体的例子:

public class Foo {
   SomeType attr1;
   SomeType attr2;
   SomeType attr3;

   public void someMethod() {
      // calculate x, y and z
      SomethingThatExpectsMyInterface something = ...;
      something.submit(new InnerFoo(x, y, z));
   }

   private class InnerFoo implements MyInterface {
      private final SomeType arg1;
      private final SomeType arg2;
      private final SomeType arg3;

      InnerFoo(SomeType arg1, SomeType arg2, SomeType arg3) {
         this.arg1 = arg1;
         this.arg2 = arg2;
         this.arg3 = arg3;
      }

      @Override
      private void methodOfMyInterface() {
         //has access to attr1, attr2, attr3, arg1, arg2, arg3
      }
   }
}

有很强的理由不公开它InnerFoo-没有其他类,图书馆也不能访问它,因为它没有定义任何公共合同,并且作者故意不希望它可供访问。但是,要使其成为100%TDD犹太洁食且无任何反射技巧就可以访问,则InnerFoo应按以下方式进行重构:

private class OuterFoo implements MyInterface {
   private final SomeType arg1;
   private final SomeType arg2;
   private final SomeType arg3;
   private final SomeType attr1;
   private final SomeType attr2;
   private final SomeType attr3;

   OuterFoo(SomeType arg1, SomeType arg2, SomeType arg3, SomeType attr1, SomeType attr2, SomeType attr3) {
      this.arg1 = arg1;
      this.arg2 = arg2;
      this.arg3 = arg3;
      this.attr1 = attr1;
      this.attr2 = attr2;
      this.attr3 = attr3;
   }

   @Override
   private void methodOfMyInterface() {
      //can be unit tested without reflection magic
   }
}

该示例仅涉及3个attrs,但是拥有5-6个OuterFoo参数是非常合理的,然后构造函数必须接受8-10个参数!在顶部添加getter,您已经有100行完全没用的代码(也需要getter来获取这些attrs进行测试)。是的,我可以通过提供构建器模式来使情况更好一些,但我认为这不仅过度设计,而且还违反了TDD本身的目的!

解决此问题的另一种方法是为类公开一个受保护的方法Foo,对其进行扩展FooTest并获取所需的数据。同样,我认为这也是一种不好的方法,因为protectedmethod
确实定义了合同, 并且通过公开该 合同 ,我现在已经对其进行隐式签名。

不要误会我的意思。 我喜欢编写可测试的代码我喜欢简洁,简洁的API,短代码块,可读性等。 但是,我不喜欢
在信息隐藏方面做出任何牺牲, 因为这更易于进行单元测试

有人能对此提出任何想法吗(总体而言,特别是特别如此)?对于给定的示例,还有其他更好的解决方案吗?


阅读 277

收藏
2020-11-26

共1个答案

小编典典

我认为您应该重新考虑使用反射。

它有其缺点,但是如果它允许您在没有虚拟代码的情况下维护所需的安全模型,那可能是一件好事。通常不需要反思,但有时没有很好的替代品。

信息隐藏的另一种方法是将类/对象视为黑匣子,并且不访问任何非公共方法(尽管这样做可以出于“错误”的原因(即答案正确但由于错误的原因)通过测试。)

2020-11-26