I am trying to implement Strategy Pattern for handling my content serialization and deserialization.
So I have four kind of requests namely CREATE, RETRIEVE, UPDATE, DELETE and for each request I want to serialize/deserialize the request and response content.
public interface ContentHandler{
String serializeRequest(Content con)();
String serializeResponse(Content con)();
Content deserializeRequest(String str)();
Content deserializeResponse(String str)();
}
Now I will have four classes:
public class CreateContentHandler implements ContentHandler{
String serializeRequest(Content con){
// .........
}
String serializeResponse(Content con){
// ........
}
Content deserializeRequest(String str){
}
Content deserializeResponse(String str){
}
}
public class RetrieveContentHandler implements ContentHandler{
//.........
}
public class UpdateContentHandler implements ContentHandler{
//.........
}
public class DeleteContentHandler implements ContentHandler{
//.........
}
Now, I have a requirement to handle different content types like JSON, XML, CUSTOM-TYPE. So serialize content in JSON way or XML way.
So I was thinking of passing a contenType variable to each function and handle content inside each function by having switch cases.
String serializeRequest(Content con, ContentType type){
// ....
switch(type){
case JSON:
case XML:
}
}
But I think this will make my serialize function big with four different type handling. I have three variables serialize/deserialize, request/response, xml/json/cutom.
How can I add new interface or classes to cater different content types to the current design ??
EDIT:
I am not doing my serialization in such a way as you mentioned using some methods:
String createStringNode(...);
String openSubbloc(...);
... // you have to analyze your switch blocks to determine the primitives
I handle JSON serialization/deserialization using a Jackson (JSON library).
So what I do is set some properties of ObjectMapper
which serializes/deserializes the data in each request.
// JSON serialization
objectMapperPropertiesBuilder = new ObjectMapperPropertiesBuilder();
objectMapperPropertiesBuilder.setSerializationFeature(SerializationFeature.WRAP_ROOT_VALUE); objectMapperPropertiesBuilder.setInclude(Include.NON_DEFAULT);
jsonPayload = JsonUtils.toJsonString(payload, objectMapperPropertiesBuilder.build());
Similarly for XML, I will be using a library and setting some properties there. So How can I implement the XMLForamtter, JSONFormatter strategy in this case ??
As for every request/response content will be different, so serialization/deserialization process will be different (different properties wil be set).
Am I missing something ??
Best Answer
The intent of the strategy pattern according to the GoF is to "define a family of algorithms, encapsulate them and make them interchangeable. The strategy lets algorithms vay independently from client that use it"
In your code you apply this pattern, making a strategy of
ContentHandler
that can be declined in different concrete content handling, depending on the requests to pursue.Problem with the switch approach:
However, in your implementation the serialization depends on the format you want to use. The way you pass a content type, using lots of
switch
blocks to produce the appropriate format, will make the code very difficult to maintain: each content handler implementation will have to provide for all kind of formats. Imagine that one day you'd like to add a new format (for example bson) : you'd need to review all theswitch
blocks of all your concrete implementation ofContentHandler
. That's a huge work, and clearly does not very well enforce separation of concerns.Alternative:
But looking at it more closely, you have here an opportunity to add a second level of strategies. Each of your switch block would correspond to a kind of primitive operation on the content to produce the format. It's another family of algorithms. You should hence make the format a strategy as well:
And then regroup all format specific primitives according to this logic:
If one day you want to support a new format, just add a new class of this kind.
You then can simplify your serializer logic:
Conclusion
Design using combination of multiple strategies is sometimes called "policy based design" It is a very powerful approach: with n kind of requests and m format, you'd write m+n classes with single responsibility, instead of writing nm classes (see other answer to your question) or writing n classes and at least nm rather redundant
cases
.