Showing posts with label OOAD. Show all posts
Showing posts with label OOAD. Show all posts

8/25/08

State machines using "Fluent interfaces/ DSL" in Java

I always found state machines very useful for describing particular aspects of object behavior. A classic example can be a shopping order: it can be "pending","in progress", "shipped", etc.
In my previous project, I started with a simple design using a transition matrix. I must confess it didn't work well. It was hard to explain and it grew more complex as new requirements were added. I though it would be simpler if I didn't use any "fancy OO stuff" and advanced idioms. I've should know better!
This time I wanted to try something different, and based in my previous experiments with internal DSLs with Scala and my (very limited) knowledge of the patters to achieve that in java, I just went ahead and tried to come up with something useful and understandable. This time seems to be working: although it has some "magic" behind curtains is only in the creation and is very easy to add or modify the configuration class for new or updated states and events. Other members of my project were able to add and remove states and transitions with only a brief explanation.

The basic classes are pretty simple: State and Event.

package statemachine;


public class Event {

final private String label;

public Event(String newLabel){

label=newLabel;

}

/**

* @return Returns the label.

*/

public String getLabel() {

return label;

}

public String toString(){

return label;

}

}


package statemachine;

import java.util.HashMap;

import java.util.Map;


public class State {

private final String label;

private final Map<Event,Transition> transitions= new HashMap<Event,Transition>();

//no public access to the constructor, must be in the same package

State(String newLabel){

this.label=newLabel;

}

void addTransition(Transition t){

transitions.put(t.getEvent(),t);

}

public State doEvent(Event e){

return (transitions.get(e)).getDestination();

}

/**

* @return Returns the label.

*/

public String getLabel() {

return label;

}

/**

* @return Returns the transitions.

*/

public Map<Event,Transition> getTransitions() {

return transitions;

}

}


You send a event to a sate and it returns the next state:
State newState=state.doEvent(event);
The State class holds a map of [event->state] (with the Transition class in the middle, for added effect :) )
As an extra safety measure, the State constructor and the addTransition method is accessible only to the package (well, unless you subclass it)
The "fluency" is provided by the Transition class (the useful methods return this to allow method chaining), so you can write:
new Transition(event).from(origin).to(destination)

package statemachine;

class Transition {

private State origin,destination;

private Event event;

public Transition(Event e){

event=e;

}

public Transition from(State orig){

origin=orig;

origin.addTransition(this);

return this;

}

public Transition to(State dest){

destination=dest;

return this;

}

/**

* @return Returns the destination.

*/

public State getDestination() {

return destination;

}

/**

* @return Returns the event.

*/

public Event getEvent() {

return event;

}

/**

* @return Returns the origin.

*/

public State getOrigin() {

return origin;

}

}



How this will look? Suppose you have a pretty simple FSM:















Using the previous pseudoDSL/Fluent interface will look like:

package statemachine;


public class FSMDef {

public final static State SUBMITTED=new State("Submitted");

public final static State OPEN= new State("Open");

public final static State CANCELLED= new State("Cancelled");

public final static State CLOSED= new State("Closed");

//Events

static class Events {

public final static Event OPEN = new Event("Open");

public final static Event CLOSE = new Event("Close");

public final static Event REOPEN = new Event("Re-Open");

public final static Event CANCEL = new Event("Cancel");

}

static {

new Transition(Events.OPEN).from(SUBMITTED).to(OPEN);

new Transition(Events.CLOSE).from(OPEN).to(CLOSED);

new Transition(Events.REOPEN).from(CLOSED).to(OPEN);

new Transition(Events.CANCEL).from(OPEN).to(CANCELLED);

}

}



Doesn't seems too complex, right?
I'm just starting with this, and probably barely skim the surface (sure it has some drawbacks), but so far it worked :)

[EDIT: Fixed the generics declarations that were eaten by the HTML format. BTW, if anybody knows a good code formatter for blog posts, let me know ]

7/10/07

Yes, design patterns are!


Lately they seems to be some kind of critique of design patterns (like singletonitis, even a discussion in comp.object). I agree that's some pattern abuse is occurring.

I just read Cedric's defense of design patterns

Mainly about this presentation from M. J. Dominus ”Design Patterns aren't” (http://perl.plover.com/yak/design/samples/slide001.html).

I believe that Dominus's presentation (or the title) misses the purpose of the GoF design patterns. They are object oriented design patterns and Alexander's patterns are architectural design patterns, (architectural in the sense of building, not software). They refer to a different design..

So, yes: design patterns, are!

What they are? Common solutions to OO design problems (not a library of C++ templates!).

New languages (or not so new) with greater flexibility make some of the GoF patterns unnecessary, because they're solved by the language itself and the problem doesn't appear.

Granted, lot of people will jump directly to apply the pattern without understanding what are the forces behind that particular pattern leading to apply the pattern in the wrong context. Maybe that's a downside of the popularity of the GoF patterns: people just apply the patterns without bothering to learn OOAD. A pattern should be the “missing piece of the jigsaw” that when put in place unifies all the forces harmoniously.

For me, one of the greatest value of the design patterns was to show the usefulness of the patter format to capture reusable knowledge: naming common solutions for recurring problems and the implications of that particular solution. As an example, (software) architectural patterns are an easy way to convey an architectural style by just mention the pattern name (e.g. layers, pipes and filters, blackboard, etc.)

Along the success of the GoF book, a whole pattern movement emerged for software and now we have architectural patterns, high level design patterns, low level design patterns, technology specific design patterns (like the J2EE patterns), analysis patterns, even business modeling patterns.

I somewhat agree that people went overboard with the patterns and need to stop overusing them and get past the 25 patterns of the book. (Every book now seems to need “patterns” in the title... and “agile” too... if I have to title a book I would name it “Agile Patterns of Refactoring Driven Design with UML”)

So, in spite of the criticism I really see value in patterns.

6/8/07

My recipe for OOAD with UML


How do you go from requirements to coding?
Everybody has different alternatives. Working on my SCEA assignment, I'm using OOAD with UML.
I choose to have an intermediate point: I'm not formally doing the OOA first (only in my head) and the OOD doesn't have much detail.
Ingredients:
  • Enough Use cases
  • A Busines domain model (small)
  • Some Aditional requirements
  • Pen and paper (could be relpaced with a whiteboard)
  • A working brain (hard to find, I usually manage without it ;-) )
  • Plenty of coffee
  • A beer

Read the use cases and requirements, stir that in your head for a while Get a piece of paper and draw the "map of the world" (existing components, outside systems, etc) to give your brain something to think about.
Create a very basic class diagram based on the domain model
Select one of most the complex use case and create a sequence diagram for it:
  • Put the actors involved
  • Choose the interface/style required to interact with them (web, Thick client, Messaging, etc.)
  • Use the identified objects from the class diagram to fulfill the requirements from the use case
  • Each object will have their responsibilities in the use case context.
  • You will need to add more objects and/or split existing ones to better distribute those responsibilities
  • Drink a cup of coffee
  • Write your assumptions
Once you fulfilled the requirements of the use case, let the sequence diagram cool for a while.
Dump all the classes you identified in the class diagram and mix them with the existing ones.
Think how you can better organize it (if smells like refactoring, you’re good, but don’t expect any “code smell” we’re far from the code). Once you’re satisfied with the result, go back to the sequence diagram and adjust it so it conforms to the new class structure. If your class diagram grows too big (maybe # of classes >7+-2) organize it in layers: put one layer of services classes, one layer of domain classes, one layer of application classes, etc.
Once the class diagram is ready, group those classes in components of the component diagram (you can add lollipop notation for the interfaces, is a nice touch)
Rinse and repeat for the other use cases.
When you’re done, you can serve the components inside deployment units in a big deployment diagram.
Now you can take a break and drink the beer!

DISCLAIMER: as with most recipes, the real process is completely different