2

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 ??

  • If your only concern is the size of your serialize function, then you don't need new interfaces or classes, you just need methods: one method for each content type. Your switch statement decides which method to call. – Robert Harvey Jul 30 '16 at 16:22
6

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.

  • +1 This goes a long way toward keeping the number of classes under control. – Mike Aug 1 '16 at 20:24
  • 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. I did not understand this line. Could you explain ? You mean I should not implement ContentHandler Strategy ?? – Siddharth Trikha Aug 2 '16 at 12:37
  • @Christophe: Please check the edits to my question – Siddharth Trikha Aug 2 '16 at 12:49
  • @SiddharthTrikha my alternative example is for illustration only. My point remains valid: everythime you have a switch, it means that you do some processing, depending on the format. What I suggested was just to abstract all these different switches and organize your code accordingly. By the way, having all your code using Jackson in the JSONFormatter class will make the rest of your code less dependent on third party libraries. Unfortunately I can't analyze all your code to see what you're doing in all these switches in order to replace them by calls to polymorphic functions. – Christophe Aug 2 '16 at 13:20
-1

To follow Open Close Principle and also Single responsibility Principle I suggest you such divisions:

public interface ContentSerializer{
    String serialize(Content str);
    String deserialize(Content str);
}

public interface RequestSerializer extends ContentSerializer{}
public interface ResponseSerializer extends ContentSerializer{}

and then have special implementation for each combination you have:

JsonCreateRequestSerializer, JsonCreateResponseSerializer,
XmlUpdateSerializer, ...

It will rapidly increase number of classes in your project, but will bring you freedom how to expand this structure, and also will give your code right order.

At the end of the day your code will still contain that big switch-case in some form at least when constructing proper instance of ContentSerializer but you can still hide it in factory:

public class RequestSerializerFactory{
    public static ContentSerializer createCreateRequestSerializer(ContentType type){}
    public static ContentSerializer createUpdateRequestSerializer(ContentType type){}
    public static ContentSerializer createRetrieveRequestSerializer(ContentType type){}
    public static ContentSerializer createDeleteRequestSerializer(ContentType type){}

} //and same for Response
  • -1 This is an anti-pattern leading to a combinatorial explosion of classes. Use a bridge pattern to separate abstraction and implementation. – Zimano Apr 1 '18 at 10:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.