Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

Example: A file, representing a serialized version of an object, needs to be read and deserialized. It would simple to implement this if we only cared about a single serialization format, but many such formats exist (JSON, YAML, etc.).

One way to handle this would be to create an abstract serializer type/interface with methods such as deserializeToArray, deserializeToObject, and so on, and pass this interface as a dependency in order to decouple any dependent classes from any concrete implementation.

This could be further generalized by having the serialization methods actually be implementations of another interface, which we'll call Transform. So here we're effectively using the Strategy pattern once again, this time encapsulating a family of algorithms for transforming one kind of data into another desired object or array format.

Is this perhaps an overuse of the Strategy pattern? Are there better ways to structure things here or patterns more fitting for this sort of problem? Are there disadvantages to using several nested Strategy patterns as in this example?

share|improve this question

migrated from stackoverflow.com Jul 10 '11 at 14:39

This question came from our site for professional and enthusiast programmers.

3  
Any pattern can be over used. –  Mitch Wheat Jul 10 '11 at 2:48
    
There is absolutely nothing that can't be overused in programming and I've inherited a Java code base that looks like it aimed to prove that. –  Erik Reppen Jul 4 '13 at 6:00

4 Answers 4

up vote 4 down vote accepted

What you're talking about is programming to abstractions, which does not imply the Strategy pattern.

Programming to abstractions is always a good practice. There's virtually no additional effort involved in providing an interface or abstract base class for a concrete implementation, and many refactoring tools can now do this automatically for you.

How abstract to make it is a different question.

In your first example, you simply have an abstract type. You could program everything to use a generic Serializer or ISerializer type and then wire up a single default implementation in an IoC container like Castle or Spring. Simple. Done.

In the second instance, though, you're designing abstract functionality. You're trying to predict how this abstract type might be used in the future, and you are probably going to be wrong. The YAGNI principle applies here; unless you have some reason to believe that your application actually needs a fully-generic "transform" interface which can turn anything into anything else, and unless you've actually scoped out the requirements for it, then you're wasting your time planning for it. Maybe you should just use one of the many existing XML transformation languages if you need that kind of flexibility.

None of this really has anything to do with the strategy pattern. The strategy pattern means that not only are you coding against an abstract type, but that you are choosing which concrete type to use based on information only known at runtime. This is neither explicit nor implicit in your question.

If you only have one implementation, then implementing a strategy pattern is absolutely overkill, unless you know for sure that you will need to support additional strategies almost immediately afterward.

But that does not preclude you from using abstract types. This is generally how most OO applications are designed now, using dependency injection for code and an IoC container for configuration. I'd go so far as to say this is more productive than coding to concrete types, because (a) it's much easier to test and mock, and (b) quickly coding an interface allows you to continue your current task without much distraction, as opposed to going off and writing a whole new class because you can't continue without it.

So definitely make use of abstract types, but don't overthink things and spend a lot of time fussing about how to generalize them. It's easier to refactor later than it is to work with a bloated interface.

share|improve this answer
    
Thanks, this definitely clears some things up for me. The first example is pretty close to what I'm actually doing. ATM I'm only using a YAML parser as the concrete type by default, but users could change this by writing their own concrete implementations and reconfiguring the IoC container. –  ubtng Jul 10 '11 at 21:16
    
I think your "how abstract to make it is a different question" pretty much hits the nail on the head as far as my major stumbling block right now. You're absolutely right, I should be just fine simply coding to abstract types, without having to try and read my users' minds down the road and plan for anything they could possibly want to do with them. –  ubtng Jul 10 '11 at 21:19
    
Patterns remind me of that thing about wise men and proverbs. –  Erik Reppen Jul 4 '13 at 6:03

Overusing design patterns is an anti-pattern in itself. YAGNI on the other hand, cannot be overused. Ask yourself if you need to support both XML and JSON today? If not, then implementing a pattern for it clearly overuse.

Learning point: only create (or refactor into) abstractions when you have an actual need for them.

share|improve this answer
3  
Oh, I beg to differ, YAGNI certainly can be overused, and often is. In this case it's the better option, but all too often I see that principle used as an excuse for exceptionally poor designs. –  Aaronaught Jul 10 '11 at 16:15
1  
@Aaron: That's like saying "we do agile, so we don't document anything" to excuse poor documentation. It's not a fault of the method or principle, it's simply people not grokking the full meaning of it. Design patterns, otoh, can be entirely understood, but still overused. –  Martin Wickman Jul 10 '11 at 16:37
    
Eh, you can justify any inanity by saying that the people whom it doesn't work for are just doing it wrong. YAGNI doesn't really define a scope or context. A professional needs to learn for himself when it does and doesn't apply. –  Aaronaught Jul 10 '11 at 16:44

No patter is needed, IMO. Keep it simple in this case. In Python:

# You may add or delete supported formats as needed.
format2serial = {'XML' : serialXml, 'JSON' : serialJSON, 'YAML' : serialYaml}
format2deserial = {'XML' : deserialXml, 'JSON' : deserialJSON, 'YAML' : deserialYaml}

def serialize(obj, format):
    try:
        serializer = format2serial(format.upper())
        serializer(obj)
    except:
        raise Exception('Unsupported serialization format: {0}.'.format(format))

def deserialize(obj, format):
    try:
        deserializer = format2deserial(format.upper())
        return deserializer(obj)
    except:
        raise Exception('Unsupported de-serialization format: {0}.'.format(format))

# You may add or delete supported [de]serializers as needed.
def serialJSON(obj): ...
def serialYaml(obj): ...
def serialXml(obj): ...

def deserialJSON(obj): ...
def deserialYaml(obj): ...
def deserialXml(obj): ...
share|improve this answer
    
Awful. Breaks the open/closed principle, while also mixing logic of totally different protocols in the same place! –  devoured elysium Jul 10 '11 at 18:14
2  
@devoured elysium, I find this comment some what academic, and contradicting YAGNI. This gets a job done. If the object and the logic for serialization and de-serialization is small enough, this will work just fine. A clean, working implementation is rather easy to re-factor. Why don't you add your own answer with a better way to do this? –  Job Jul 10 '11 at 18:23

I think one factor to take into account when talking about intense use of patterns is the project and/or application size. Let's say our project consists of 10 teams each 5-6 members, then yes, implementing all kinds of patterns from the beginning may be a good idea.

If you project starts small and you don't know how it will evolve, I recommend to prepare for certain patterns. E.g. you can easily prepare for the Strategy pattern by using the Factory Method Pattern:

Serializer s = SerializerFactory.getSerializer("JSON");
s.serialize(someObject);

A first implementation of the factory may look like this:

public class SerializerFactory {

    public static Serializer getSerializer(String protocol) {
          return new DefaultSerializer(protocol);
    }
}

And the DefaultSerializer:

public class DefaultSerializer implements Serializer {
    private String protocol = null;

    public DefaultSerializer(String protocol) {
          this.protocol = protocol;
    }

    public void serialize(Object someObject) {
          if ("JSON".equals(this.protocol)) {
               // your logic here.
          } else if ("SOAP".equals(this.protocol)) {
               // your logic here.
          }...
    }

}

This brings you into the position to "easily" implement the Strategy Pattern at a later time by only touching the factory and the DefaultSerializer... my 2 cent

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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