状态依赖性和线程安全性
我有Struts 1动作类(动作是struts 1中的设计单例),需要收集一些数据,然后将它们组合成单个响应。 我想将所有的响应生成逻辑提取到单独的类中
ResponseBuilder
通常情况下,我会把ResponseBuilder作为一个字段并为其设置(例如,用于测试)。 我的响应生成器如下所示
class JsonResponseBuilder implements ResponseBuilder {
public void addElement(String key, Object value) {
...
}
public String buildResponse() {
// build response from data collected
}
}
有了这样的实现,我不能这样做,因为线程安全问题在这里。
我该如何改变这种设计才行? 这里的工厂模式是否适用? 我的意思是使用
ResponseBuilderFactory
作为依赖关系并且这样调用它:
ResponseBuilder builder = factory.getBuilder();
builder.addElement(...);
...
String response = builder.build();
从设计和可测试性的角度来看是否可行? 如果没关系。 如何为此编写测试代码? 模拟工厂? 模拟建造者?
工厂会工作。 你基本上正在用工厂做一种依赖注入的形式。 当你实例化或初始化你的动作时,你可以设置工厂:
public void setResponseFactory(ResponseBuilderFactory factory) {
this.responseFactory = factory;
}
并且在工厂中,只需在需要时返回JsonResponseBuilder
的新实例。 确保你不将JsonResponseBuilder
的实例作为实例变量存储在你的动作中; 它必须保持在您正在使用的方法的本地,或作为方法参数传递。
至于测试,用一个模拟工厂替换工厂变得容易,该工厂返回一个模拟的ResponseBuilder。 有许多库可以做到这一点,例如Mockito或JMock。 它们都能很好地与JUnit和TestNG配合使用。
编辑:
您需要将ResponseBuilderFactory作为接口,如下所示:
public interface ResponseBuilderFactory {
public ResponseBuilder getResponseBuilder();
}
当你进行测试时,只需创建一个返回ResponseBuilder模拟的类:
@Test
public void testMyAction() throws Exception {
ResponseBuilderFactory mockFactory = new ResponseBuilderFactory() {
public ResponseBuilder getResponseBuilder() {
ResponseBuilder builder = context.mock(ResponseBuilder.class);
// set up mock behaviour
return builder;
}
}
}
所以你不注射一个模拟工厂,只是一个返回模拟的工厂。
另请参阅依赖注入VS工厂模式。
编辑2:
如果你可以设法重新编写你的JsonResponseBuilder
以便它不保持状态,那么你可以避免整个工厂混乱在一起,只使用你的原始方法。 不保持状态的对象本质上是线程安全的。
只需在您的Action Service方法中实例化ResponseBuilder。 这样它将是一个局部变量(而不是一个单独的类成员),每个线程都将有一个独特的ResponseBuilder新实例。
您可以单独测试ResponseBuilder,并且您也可以像其他任何操作一样正常测试您的操作(如果需要,可以模拟ResponseBuilder)。
使用工厂模式构建ResponseBuilder是另一回事,与您的问题imo无关。 如果实例化ResponseBuilders是昂贵的并且它们是可重用的,那么您可能希望使用ResponseBuilders池。
在Action中使ResponseBuilder成为变量的那一瞬间,它就在所有请求中共享。 这意味着你必须同步它,以确保它的可变状态(如果有的话)是线程安全的。
另一种方法是在调用每个动作的方法内部创建一个新的ResponseBuilder。 您以这种方式为每个请求创建一个实例。
链接地址: http://www.djcxy.com/p/77759.html