Currently I have a class, TransactionData
, which is just a little bit more than a POJO. I build the object from an HTTPServletRequest
. What I do:
public class TransactionData
{
// ...
public TransactionData(HttpServletRequest request) throws IOException
{
// do actual work here
}
}
There many WTF here, the most disturbing one is that the object TransactionData
is tightly coupled to HTTPServletRequest
. What I thought: create an interface, TransactionDataExtractor
, with an extract()
method, so that I might implement different classes to build the object.
public interface TransactionDataExtractor
{
public TransactionData extract();
}
Bu开发者_JS百科t how do I pass the stuff needed to build the TransactionData to every implementation? The firt thing that came to mind was to use the different constructors, like this:
public class TransactionDataExtractorRequest implements TransactionDataExtractor
{
private HttpServletRequest httpRequest;
public TransactionDataExtractorRequest(HttpServletRequest httpRequest)
{
this.httpRequest = httpRequest;
}
public TransactionData extract()
{
// do whatever is required
}
}
But in this case whenever I need to build a new TransactionData object I have to create a new TransactionDataExtractorRequest. An implicit dependency I don't like at all.
The other alternative I could think of was passing an Object parameter to extract()
and cast it whenever required, giving up the type safety and introducing a lot of boiler plate ugly code
public TransactionData extract(Object o)
{
HttpServletRequest h;
if (o instanceof HttpServletRequest)
{
h = (HttpServletRequest)o;
}
//...
}
I don't know if I have made myself clear. I do feel like I'm missing something, I know the solution is very simple but I can't get hold of it. Any thoughts? TIA.
EDIT: the problem might even be that my hunch is completely wrong and I may dismiss it without any regret
If your only problem is ensuring type safety when passing the source object to extract()
, you can use generics:
public interface TransactionDataExtractor<E> {
public TransactionData extract(E source);
}
public class TransactionDataExtractorRequest
implements TransactionDataExtractor<HttpServletRequest> {
public TransactionData extract(HttpServletRequest source) { ... }
}
I do feel like I'm missing something ... Any thoughts?
Well my thought is that you are attempting to solve a problem that isn't really a problem. There's no obvious (to me) reason why the coupling you are trying to get rid of is actually harmful. Certainly, your attempts to remove the coupling are not making the code any easier to understand.
In case you rely on request parameters only, you can get request.getParameterMap()
and use the Map
instead. (if you need headers - getHeaders()
)
Creating/reusing a TransactionDataExtractorRequest
instance isn't a problem, IMHO. You'd need to somewhere distinguish between the parameter types anyway and if you decouple TransactionData and the parameter types by using some sort of factory, what's wrong with that?
I'm still not convinced that much is gained by removing the dependency on HttpServletRequest
, but I would suggest something along the lines of:
public class TransactionData {
public TransactionData(TransactionDataOptions options) throws IOException {
// do actual work here
}
}
//TransactionData wants some state that it currently gets from a HttpServletRequest,
//figure out what that state is, and abstract an interface for accessing it
public interface TransactionDataOptions {
//getters for things that TransactionData needs
}
//take all the code that pulls state out of the HttpServletRequest, and move it here
public class TransactionDataHttpOptions implements TransactionDataOptions {
private HttpServletRequest request;
//getter implementations that pull the required information out of the request
public TransactionDataHttpOptions(HttpServletRequest request) {
this.request = request;
}
}
//now you can also do this, and use TransactionData even without a HttpServletRequest
public class TransactionDataMapOptions implements TransactionDataOptions {
private Map<String, Object> map;
//getter implementations that pull the required information out of the map
public TransactionDataHttpOptions(Map<String, Object> map) {
this.map = map;
}
}
If you go this route, then TransactionDataHttpOptions
is the only object with a dependency on HttpServletRequest
. And since it is basically a wrapper that is intended to work with a HttpServletRequest
I think that should be fine.
精彩评论