stateful dependency and thread safety
I've Struts 1 action class (actions are singletons by design in struts 1) that needs to collect some data and then combine them all into single response. I'd like to extract all the response generation logic into separate class called
ResponseBuilder
Normally I'd put ResponseBuilder as a field and have setter for it (eg for testing). My response builder looks as follows
class JsonResponseBuilder implements ResponseBuilder {
public void addElement(String key, Object value) {
...
}
public String buildResponse() {
// build response from data collected
}
}
With such implementation I can't do it due to thread safety issues here.
How can I change this design to be ok? Is Factory pattern here applicable? I mean is using
ResponseBuilderFactory
as a dependency and calling it that way:
ResponseBuilder builder = factory.getBuilder();
builder.addElement(...);
...
String response = builder.build();
is ok from design and testability point of view? If it is ok. how to write test code for this? Mock factory? Mock builder?
A factory would work. You're essentially doing a form of Dependency Injection with a factory. When you instantiate or initialise your action, you would set the factory:
public void setResponseFactory(ResponseBuilderFactory factory) {
this.responseFactory = factory;
}
and in the factory, just return a new instance of JsonResponseBuilder
when you need it. Make sure that you do not store your instance of JsonResponseBuilder
as an instance variable in your action; it must remain local to the method you're using, or passed around as a method parameter.
As for testing, it becomes easy to replace the factory with a mock factory that returns a mock ResponseBuilder. There are many libraries to do this, like Mockito or JMock. All of them work well with JUnit and TestNG.
Edit:
You'd need to have ResponseBuilderFactory as an interface, like:
public interface ResponseBuilderFactory {
public ResponseBuilder getResponseBuilder();
}
When you do your testing, just create a class that returns a mock of your 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;
}
}
}
So you're not injecting a mock factory, just a factory that returns mocks.
Also, see Dependency Injection vs Factory Pattern.
Edit 2:
If you can somehow manage to re-code your JsonResponseBuilder
so that it doesn't maintain state, then you could potentially avoid the whole factory mess all together and just use your original approach. Objects that do not maintain state are inherently thread-safe.
Just instantiate your ResponseBuilder within your action service method. That way it will be a local variable (instead of a singleton class member) and each thread will have a distinct fresh instance of ResponseBuilder.
You can unit test the ResponseBuilder separately, and you can also test your action normally as with any other action (mocking the ResponseBuilder if needed).
Using a factory pattern to build your ResponseBuilder is a different matter, not related to your issue, imo. If instantiating ResponseBuilders is expensive and they are reusable, then you may wish to use a pool of ResponseBuilders.
The minute you make your ResponseBuilder a member variable in an Action, it's shared across all requests. That means you'll have to synchronize it to ensure that its mutable state, if any, is thread safe.
Another way to do it is to create a new ResponseBuilder inside the method called for each action. You create an instance for each request that way.
链接地址: http://www.djcxy.com/p/77760.html上一篇: asp.net核心服务定位器如何避免在cosole应用程序中
下一篇: 状态依赖性和线程安全性