Thursday, December 31, 2009

Design patterns in Go programming language

It has been said that design patterns is a symptom of language deficiency. If you need a pattern that indicated a language feature should have existed instead.

Here I'd like to show how many class design patterns can be simplified in the Go programming language to look less like patterns and more like language features. This post was inspired by previous discussions online (Joe Gregorio) about how design patterns were rarely talked about in the Python community, due to the simple fact that in dynamic languages, the need for specific patterns isn't really there. The language often supports directly what you simulate with a pattern. The examples I will use will mostly be related to computer games. Meaning examples will be about classes and objects you typically find in a game engine

Command Pattern

Let's say we have a game editor which lets us place sprites in a game world.

The code below shows how we might imagine this could be done in C++. When the user click on a position, that position pos is given to a function along with the sprite which will be placed there.


class Command {
 virtual void Do() = 0;
 virtual void Undo() = 0;
};

class PlaceSpriteCmd : public Command {
 Sprite* _sprite;
 Point _oldpos, _newpos;
 
public:
 PlaceSpriteCmd(Sprite* sprite, Point pos) : 
  _sprite(sprite), _newpos(pos) 
 {
  _oldpos = sprite->position();
  
 }
 
 void Do() {
  sprite->setPosition(_newpos);
 }
 
 void Undo() {
  sprite->setPosition(_oldpos);
 }
 
};

static CommandStack _undoStack, _redoStack;

void PerformUndoableCmd(Command *cmd) {
 cmd->Do();
 _undoStack.push(cmd);
}

void UndoLastCmd() {
 Command* cmd = _undoStack.pop();
 cmd->Undo();
 _redoStack.push(cmd); 
}

void onPlaceSprite(Sprite* sprite, Point pos) {
 Command *cmd = new PlaceSpriteCmd(sprite, pos);
 PerformUndoableCmd(cmd);
}

The code is written with clarity in mind, so it is not meant to compile. It has no error checking, would probably leak memory etc.

It should thus be clear that supporting Undo in C++ require us to:
  1. Create a Command base interface.
  2. For each command we need a concrete subclass of this interface.
  3. Three methods have to be implemented at least in these subclasses: Constructor, Do and Undo.
  4. We need to define some member variables in concrete subclass to store enough information to be able to perform the undo. E.g. in the case above we store the both current and new position to support undo and redo.

Since Go supports closures, functions are essentially first class objects which is in many ways what we simulate with Command subclasses, this becomes a lot simpler:


func MakePlaceSpriteCmd(sprite *Sprite, newpos Point) (undo, redo func()) {
 oldpos := sprite.Position()
 undo = func() { 
  sprite.SetPosition(oldpos) 
 }
 
 redo = func() { 
  sprite.SetPosition(newpos) 
 }
 
 return
}

type Command struct {
 undo, redo func()
}

var undoStack, redoStack CommandStack

func PerformUndoableCmd(undo, redo func()) {
 redo()
 undoStack.push(Command{undo, redo})
}

func UndoLastCmd() {
 cmd := undoStack.pop()
 cmd.undo()
 redoStack.push(cmd); 
}

func onPlaceSprite(sprite *Sprite, pos Point) {
 undo, redo := MakePlaceSpriteCmd(sprite, pos)
 PerformUndoableCmd(undo, redo);
}

It might not look a lot simpler, but that is mainly because of a certain fixed amount of code that has to be both places. What should hopefully be clear from the example however is that the difference becomes more noticeable as one starts adding commands. In the C++ case that involves creating a whole class.

Strategy Pattern

The main motivation behind the strategy pattern is to avoid an explosion in number of subclasses and code duplication. E.g. consider a real time strategy game with sprites for Allied tanks, Axis tanks and Soviet tanks. All the sprites are rendered differently, and might have different amounts of armor, gas tank volume etc. The tanks might have different behavior like cautious, aggressive, sneaky etc. The naive approach would be to create a subclass of the allied tank for each of those behaviors. However we would have to do this for each tank type even though the behavior code might be exactly the same. This leads to an explosion in subclasses.

Hence the use of the strategy pattern, which involves encapsulating the behavior into a separate objects and assign different behavior objects to tank objects at runtime. The previous post on Go vs Java touches upon this with the discussion of steering behaviors. Essentially steering behaviors could be thought of as the same as behaviors for sprites. The Go vs Java example shows how a language like Go with first class functions simplifies greatly the creation of strategy objects, in much the same way as we demonstrated in the Command pattern section above.

In Java and C++ we would have needed to create subclasses such as Agressive, Sneaky and Cautious of an interface, say Behavior. Go gets away with defining closures.

We could even apply the strategy pattern to how sprites are drawn:
type Sprite struct {
 pos Point
 draw func(pos Point)
}

func (sprite *Sprite) Draw() {
 sprite.draw(sprite.pos)
}

Factory Pattern

In the Design Patterns book by the Gang of Four, different construction patterns are demonstrated using the example of a maze. The idea is that a maze consists of a number of connected rooms of different types. The reason for using some kind of factory is that we might want to change the types of rooms used but we don't want to have to change the code that creates the maze by instantiating room objects and connecting them. Here is an example of what the maze construction code might look like in C++:


void BuildMaze(RoomFactory* factory) {
 Room* a = factory->CreateRoom("Prison");
 Room* b = factory->CreateRoom("GuardRoom");
 Connect(a, b); 
}

In Go we don't need to use an instance of a class at all. It is more convenient to simply use a function object:


func BuildMaze(createRoom func(roomType string) Room) {
 a := createRoom("Prison");
 b := createRoom("GuardRoom");
 Connect(a, b); 
}

But in practice it goes beyond this. Those familiar with more dynamic OOP languages like Smalltalk and Objective-C know that classes are objects and can thus be passed around in the code. In languages like C++ and Java we usually simulate this by creating a factory class for each class we want to be able to pass around as if the class was an object.

In Go we can achieve much the same without extra code. The idiomatic way of creating an instance of a struct is to call a free function. This is because Go does not support constructors. So instead of coding constructors to instantiate structs you code free functions. The beneficial side effect of this is that because functions are first class objects, these creation functions can be passed around in similar fashion as Objective-C class objects. Constructors are not regular functions and can thus not be passed around in this fashion. Thus separate factory functions or classes needs to be created in languages like Java and C++.

Conclusion

While writing this blog entry, I tried to compare Go with Python. It became apparent that while Go can achieve a lot of the same simplicity as Python, it still can't do all patterns as simple as in Python. The most problematic pattern I noticed was subject-observer. In Joe Gregorio's presentation on the lack of design patterns in Python, it is written as this:

class Point(object): 
    def __init__(self, x, y): 
        self.x = x 
        self.y = y 

    def scale(self, n): 
        self.x = n * self.x 
        self.y = n * self.y 
def notify(f): 
    def g(self, n): 
        print n 
        return f(self, n) 
    return g 
Point.scale = notify(Point.scale) 

p = Point(2.0, 3.0) 

p.scale(2.5) 

Which can't be easily reproduced in Go. However the other patterns mentioned by Joe Gregorio: Iterator, Factory and Strategy should be just as easy as I hope I demonstrated clearly here. Another aspect which I haven't seen anybody write about in detail is lack of refactoring in Python and Ruby code bases. I am not saying it doesn't happen but I notice a couple developers who develop in both C++, Java and Ruby point out that they almost never refactor their Ruby code, while it is frequent occurrence in Java and C++. The reason being given that the need seldom seem to arise. I think it would be interesting to see in the future when more experience is gained from using Go, what the experience will be for Go. Will there be a lot of design patterns and refactoring or will the experiences be more similar to that of Python and Ruby developers?

Wednesday, December 02, 2009

Go vs Java

On the surface Go and Java might seem to have a lot in common. They are both modern C/C++ like languages with garbage collection, supporting object oriented programming.

But beyond that there are quite a lot of differences. I will not highlight so much of Java's strengths compared to Go, as Java has been around for a long time. What is more interesting is why a developer should want to choose Go over Java, given Java's ubiquity, large number of frameworks, tools etc.

First issue is efficiency both with respect to memory usage and performance. Go allows much more low level tuning similar to C. A problem in Java is that all types except primitive types are reference types. That means related data can't be stored in one location. E.g. say we have a Car object with 1 Engine object, 4 Wheel objects etc. All those objects are stored in different locations in memory. While in C or Go you could store all the Car related data as one continuous block of memory. Why is that important? In modern computers CPU's can process data a lot faster than it can be feed to it by regular RAM memory. Due to this frequently used parts of the main RAM memory are stored in a super fast memory called cache. For caches to be efficient related data needs to be close in address space. That is hard to achieve in Java.

An example of this in practice is the distributed version control system git. It is known to be very fast. It is written in C. A Java version JGit was made. It was considerably slower. Handling of memory and lack of unsigned types was some of the important reasons.

Shawn O. Pearce wrote on the git mailinglist:
JGit struggles with not having an efficient way to represent a SHA-1. C can just say "unsigned char[20]" and have it inline into the container's memory allocation. A byte[20] in Java will cost an *additional* 16 bytes of memory, and be slower to access because the bytes themselves are in a different area of memory from the container object. We try to work around it by converting from a byte[20] to 5 ints, but that costs us machine instructions

Like C, Go does allow unsigned types and defining data structures containing other data structures as continuos blocks of memory

Method calls

Before reading this it might be good to read Scott Stanchfield's article on why all java method calls use pass-by-value, and that there is no such thing as pass-by-reference in Java. However as mentioned Java does not support value types for other than primitive types. This causes problems for method calls. One problem is that small objects like a Point might often be faster to copy in a method call, rather than copy their reference which is what Java does. More importantly perhaps is that value semantics is often easier understood. E.g. it would be natural to assume that Point would be a value type. If I pass a Point object to a function I don't expect my point to be modified by called function. And yet that can easily happen in Java.

Too strong focus on OOP

Since the decision was made that Java would have no free functions (even though static methods in a way is free functions) this has caused the Java designer to come up with very cumbersome syntax to deal with problem that would have been best handled with free functions. Instead of closures Java got nested classes:

 myButton.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            frame.toFront();
        }
    });
The same thing is achieved a lot less verbosely in Go using closures:
 myButton.addActionListener(func(e ActionEvent) {
  frame.toFront();
 });

Actually the Java version requires even more code, because the ActionListener class needs to be defined somewhere. The function object passed in the Go example is defined right were it is used.

Why Java code end up being considerably more verbose than Go code

When you start building more complicated things this problem starts adding up, causing excessive amounts of code to be needed in Java, while short readable code can be used in Go for the same thing. Consider this example. For a game engine I was writing I used Lua as a script language. Algorithms for planning movement of Non player characters was based on combining different functions describing different behavior. Without a lot of background information the code below is not easy to follow. But bare with me:

local flank = Geometry.makeFlank(player, 10)
local seek = Geometry.makeSeek(player:position())
local combo = Geometry.combineBehavior({0.001,seek}, {1,flank})

flank, seek and combo are functions created by other functions makeFlank, makeSeek etc. The combo function is created by combining the flank and seek functions. Basically each function takes two arguments referred to as s0 and s1, which denotes a orientation and position in space. Below is the code that creates the seek function:

function Geometry.makeSeek(target)
  -- Goodness of trajectory from state 's0' to 's1'
  function seek(s0, s1)
    local p0 = s0:position()
    local p1 = s1:position()
    local d1 = s1:direction()
    
    -- direction to target
    local dir_target = (target-p1):unit()  
    
    -- direction of from current point to next on path
    local dir_path = (p1-p0):unit()   
    return 0.25*(1 + d1*dir_target)*(1 + dir_path * dir_target)
  end
  return seek
end

The details of the code is not important. It is mainly vector operations used to calculate how desirable s1 is with respect to getting the non player character to the goal position target. The candidate s1 position and orientations are produced elsewhere by considering places in space which does not cause collision with other objects etc. The code below shows how we would do this in Java. Since Java does not have closures we have to use classes:

interface Behavior {
  public float goodness(State s0, State s1);
  public void  add(Behavior b, float weight);  
}

class Seek implements Behavior {
  Vec2 target;
  
  public Seek(Vec2 target) {
    this.target = target;
  }
  
  public float goodness(State s0, State s1) {
    Vec2 p0 = s0.position();
    Vec2 p1 = s1.position();
    Vec2 d1 = s1.direction();
        
    // direction to target   
    Vec2 dir_target = (target.minus(p1)).unit();

    // direction of from current point to next on path
    Vec2 dir_path = (p1.minus(p0)).unit();
    return 0.25*(1.0 + d1.dot(dir_target))*(1.0 + dir_path.dot(dir_target));    
  }  
}

The different behaviors will then be combined as follows:

Behavior flank = new Flank(player, 10);
Behavior seek  = new Seek(target);
Behavior combo = new Combo();
combo.add(flank, 1.0);
combo.add(seek, 0.001);

With Go we can write the code more like we did in the dynamic script language Lua.

func makeSeek(target Vec2) func(s0, s1 State) float {
  func seek(s0, s1 State) float {
   p0 := s0.position();
   p1 := s1.position(); 
   d1 := s1.direction(); 

   // direction to target   
   dir_target := (target.minus(p1)).unit();

   // direction of from current point to next on path
   dir_path := (p1.minus(p0)).unit();
   return 0.25*(1.0 + d1.dot(dir_target))*(1.0 + dir_path.dot(dir_target));    
  }
  return seek; 
}

The the different behavior can be combined in much the same way as it was combined in Lua. The last statement can be done in many ways. The code below is valid since Go supports functions with arbitrary number of arguments, but it can't be properly type checked at compile time.

flank := makeFlank(player, 10);
seek  := makeSeek(target);
combo := makeCombo(flank, 1.0, seek, 0.001);

For stronger type checking one might want to do something like the code below, which requires defining a struct Elem with a function pointer and float member.

combo := makeCombo(List{Elem{flank, 1.0}, Elem{seek, 0.001}});

Doing something similar to the Java way is also possible:

var funcs FuncList;
funcs.Add(flank, 1.0);
funcs.Add(seek, 0.001);
combo := makeCombo(&funcs);

What should be apparent is how much boilerplate code one has to write when making Java programs:

  1. Every method needs public in front
  2. Type has to be repeated for every argument in method definition.
  3. An interface has to be defined every time we want to simulare combining two functions.
  4. The type of a variable has to be specified every time even though the compiler should be able to figure it out based on assignment

The lua code goes on to create new functions:

local eval = Geometry.makeEval(combo, 0.5, 0)
local larger = makeBinary(eval, greaterThan)

Since eval and larger have different function signatures, we would need to create two new interfaces in Java and two class implementing them. Each class will have two methods and some member variables. In contrast the Go solution would only require a total of 4 new functions. No classes or interfaces needs to be created.

Conclusion

As stated earlier the aim of this article was to make the case for Go over Java, so you have to excuse my bias. Java has improved a lot over the years. It used to be extremely verbose for simple things like reading and writing to console. Java 5 improved upon that a great deal. But as these examples show, Java still requires a lot of boilerplate code that doesn't add anything to readability and ability to abstract or reason about the problem you are trying to solve.

I have not touched upon other clear Go advantages like compile time, channels for easing concurrency programming etc. I wanted to show that even without Go's most touted features here is a lot to gain. As new versions of Java comes out new features are added which addresses deficiencies in the language. However the problem is that gradually Java loses some of its original appeal which was that it was a very simple language. Go starts with a small feature set than present Java, perhaps more comparable to the original Java. However the features are better selected so similar kind of expressive power as todays Java can be expressed with far fewer features. I think that is worth somthing