我很多时候都参与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应按以下方式进行重构:
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本身的目的!
OuterFoo
解决此问题的另一种方法是为类公开一个受保护的方法Foo,对其进行扩展FooTest并获取所需的数据。同样,我认为这也是一种不好的方法,因为protectedmethod 确实定义了合同, 并且通过公开该 合同 ,我现在已经对其进行隐式签名。
Foo
FooTest
protected
不要误会我的意思。 我喜欢编写可测试的代码 。 我喜欢简洁,简洁的API,短代码块,可读性等。 但是,我不喜欢 在信息隐藏方面做出任何牺牲, 因为这更易于进行单元测试 。
有人能对此提出任何想法吗(总体而言,特别是特别如此)?对于给定的示例,还有其他更好的解决方案吗?
我认为您应该重新考虑使用反射。
它有其缺点,但是如果它允许您在没有虚拟代码的情况下维护所需的安全模型,那可能是一件好事。通常不需要反思,但有时没有很好的替代品。
信息隐藏的另一种方法是将类/对象视为黑匣子,并且不访问任何非公共方法(尽管这样做可以出于“错误”的原因(即答案正确但由于错误的原因)通过测试。)