December 05, 2010

Documenting Design Patterns with Annotations

« Embed Ivy - How to use Ivy with Java | Main | Complex Sorting with Java Comparator »

What's the idea

Annotations in Java do a great job for developers to keep all information around our code at a single place. And the place where this information is kept is the one which is most natural for us, the source code itself.

Think about how annotations like @Entity, @Webservice and @Stateful changed the way developers write source code, having all available at the source code level what is needed to run the code in our infrastructure and what we need to know when we read the source code to understand what's happening when we analyze and debug the code. No more deployment descriptors and and no much reason for any external source code documentation.

But wait, there's one thing missing. When we wrote the code in the beginning, we had had some ideas on the design of our classes, interfaces and methods. We had some problems to solve and we came up with some clever solutions. Sometimes we took the time and wrote some comments on the ideas and solutions in javadoc, and sometimes not. And sometimes we discussed it with our team and in the end found a more general solution for our problem, which solved the design problem in a elegant way. We had identified a pattern to our problem and since a lot of clever guys like the GoF had already written books on the patterns we were happy to apply the well documented design pattern to our problem. Everybody knew what we were doing and why we were doing it this way by a single word: the name of the design pattern. Call it a Composite, and almost every developer knows what is meant. The rest should really look it up at the GoF book.

And now, time moved on, I hardly remember the problem we solved at that time and even vaguer are the memories how we did it. The team has changed and what we had coded then is now in the hands of some guys of another department. And they keep asking questions, what's happening here at this class, and why is this so complicated, what's the idea behind this? Ok I can't remember, so lets look at the code again. Ah, this class here, and that one there, that are the keys. Here, look at this, that's a Command Pattern, here's the Command, that is the ConcreteCommand and that one is the Client. The picture's is restored in mind and everything's easy again.

Command Pattern from the GoF

We should have documented this in the beginning, shouldn't we?

And that's where annotations and the idea that only the source tells the true story in the end come back again. Let's use annotations to make our decisions on code design permament. Have a look at the source code and it spells the design pattern we have put into it.

How does this looks like

Let's say we have some management console which can configure a lot of of stuff in our software. And it has menu links which are disabled when we can not change a feature.

package example;

import org.jpatterns.gof.CommandPattern;

@CommandPattern.Command(comment = "generic interface for all configuration tasks", 
    undoable = false, 
    participants = { Client.class, CreateUserCommand.class, DeleteUserCommand.class })
public interface ConfigurationCommand {
  boolean canChange(ConfigurationParameters parameters);

  Result change(ConfigurationParameters parameters);
}
	
Pretty nice, it says: this code is the Command interface from the GoF CommandPattern, our commands can not be undone, and that other participants in this design patterns are the classes Client, CreateUserCommand, DeleteUserCommand. That's what we need to know. And when we look at the other classes it looks like this:
import org.jpatterns.gof.CommandPattern;

@CommandPattern.ConcreteCommand(
  comment="concrete command implementation", 
  participants={ConfigurationCommand.class, Client.class})
public class CreateUserCommand implements ConfigurationCommand {
  public CreateUserCommand(Receiver receiver) {
    this.receiver = receiver;
  }
  
  @Override
  public boolean canChange(ConfigurationParameters parameters) {
    return true;
  }

  @Override
  public Result change(ConfigurationParameters parameters) {
    User user = userManager.createUser(parameters);
    Result result = new Result(user);
    receiver.action(result);
    return result;
  }

  private UserManager userManager;
  private Receiver receiver;
}	

import org.jpatterns.gof.CommandPattern;

@CommandPattern.Client(comment="react on user creation")
@CommandPattern.Invoker(comment="trigger user creation")
public class Client implements Receiver {
  
  public void action(Result result) {
    if (result.isSuccess()) 
      System.out.println("User created: " + result.getObject());
    else 
      System.out.println("User creation failed: " + result.getErrorMessage());
  }
  
  public void createUser(ConfigurationParameters parameters) {
    ConfigurationCommand command = new CreateUserCommand(receiver);
    if (command.canChange(parameters)) {
      command.change(parameters);
    }
  }
  
  private Receiver receiver;
}

import org.jpatterns.gof.FacadePattern;

@FacadePattern(comment="service facade for user management", participants={CreateUserCommand.class})
public interface UserManager {
  User createUser(ConfigurationParameters parameters);
  User deleteUser(ConfigurationParameters parameters);
}
	
That tells us pretty much all we wanted to know, even our shortcut to the GoF pattern is here. We decided that we could put the Invoker and the Client in one class, so there are two annotations for this class because it servers two roles in the pattern. And the UserManager is a Facade to the really big and complex user management system.

Command Pattern from the GoF

And looking at the javadoc (in Eclipse just hover over the annotation) we even get some references where to look up all the details of the design pattern.

How to do your own patterns

When your organization has already drafted or documented a set of own patterns, you can easily define them as annotations as well. Pattern annotations are just interfaces and you should remove any dependencies for the runtime with @Retention(RetentionPolicy.RUNTIME). And of course you can define anti-patterns as well. So if you there are some guys in your department who love to do everything from scratch before using other people's solutions, mark the code with @NotInventedHere :-)

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@DesignPattern(type = Type.ENTERPRISE)
public @interface NotInventedHere {
  Class[] participants() default {};
  String comment() default "";

  @Retention(RetentionPolicy.RUNTIME)
  @Target(ElementType.TYPE)
  @Documented
  public @interface Redundancies {
    Class[] participants() default {};
    String comment() default "";
  }
}
	

References

About the Author

Johannes Beck
professional record of 12 years in the software industry, especially in enterprise systems and Java
software architect at 1&1 Internet AG
37 years old, living in Karlsruhe, Germany.

Technorati Tags:

Posted by johannes.beck at 9:45 AM in Design Pattern

 

[Trackback URL for this entry]

Comment: Doug at Mo, 6 Dez 11:10 PM

I don't know what other may think but this is kind of lame!
Should I say this is "pattern oriented programming"? Patterns are vehicles to achieve maintainability and re-usability. But when I heard someone says "I implemented pattern X here and pattern Y there in this code" I feel like he just gets out of school. I've never heard any programming experts claim they use this pattern and that pattern. Even though, looking at the code we all know they use design pattern. Calling out design pattern should only be applied in school and for beginner. Everyone is welcome to call it out. But experts normally don't :))

Comment: James at Di, 7 Dez 9:19 AM

I agree with Doug! and what happened to good old fashioned javadoc anyway? Might be good to use this if you are a lecturer though to get your students to show they understood they were using a design pattern rather than lucking out! :)

Comment: Mike at Di, 7 Dez 1:03 PM

Concur with Doug. And additionally you creates unwanted dependencies here: ConfigurationCommand becomes dependent on Client.class, CreateUserCommand.class and DeleteUserCommand.class which is wrong. CreateUserCommand becomes dependent on Client.class which is wrong. And UserManager violates encapsulation by creating a dependency to CreateUserCommand.class. This whole idea is just plain wrong from an object oriented point of view.

Comment: codesmuggler at Sa, 15 Jan 6:09 PM

It's nice way of documenting code which includes design patterns.
I'm curious - are most people you told about this tool working with it - or just keep javadocing?
What are their reasons?

Comment: codesmuggler at Sa, 15 Jan 6:10 PM

It's nice way of documenting code which includes design patterns.
I'm curious - are most people you told about this tool working with it - or just keep javadocing?
What are their reasons?

Your comment:

(not displayed)
 
 
 

Live Comment Preview: