Java – Implementing strategy pattern multiple variables

design-patternsjavaobject-orientedobject-oriented-designstrategy

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 the switch blocks of all your concrete implementation of ContentHandler. 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:

public interface SerialFormatter {
    String createStringNode(...);  
    String openSubbloc(...);  
    ... // you have to analyze your switch blocks to determine the primitives
};

And then regroup all format specific primitives according to this logic:

class JSONFormatter implements SerialFormatter { ... }; 
class XMLFormatter implements SerialFormatter { ... }; 

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:

String serializeRequest(Content con, SerialFormatter fmt){
   // ....
   String s = fmt.openSubbloc ("ID") + 
       fmt.createStringNode ("Name", con.name() ) +
       ...
       fmt.closeSubbloc("ID"); 
   return s;  
 }

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.

Related Topic