Monday, December 15, 2008

Composite Oriented Programming Part 2

I made a slightly more complicated version of the hello world example. This time I made two composite implementations of a Speaker interface.

public interface Speaker
{
public void setSpeech(char[] speech);

public char[] speak();
}

Then I changed how the data is handled as I didn't want two different templates, HelloData and ByeData. So I made a PropertiesMixin template which contains a simple way to set and get properties as char[]s.

public template PropertiesMixin()
{
private char[][char[]] properties;

public void set(char[] name, char[] value)
{
properties[name] = value;
}

public char[] get(char[] name, char[] defaultValue = null)
{
if (name in properties)
{
return properties[name];
}
return defaultValue;
}
}

This can be used to add properties support to a class. There is room for improvement, for example some how use the tango.text.Properties class to save and load the properties.

Then the real meat are the new HelloMixin and ByeMixin, which handles the heavy lifting.

public template HelloMixin()
{
private static const char[] HELLO = "hello";

mixin PropertiesMixin;

public void setSpeech(char[] speech)
{
set(HELLO, speech);
}

public char[] speak()
{
return get(HELLO);
}
}

public template ByeMixin()
{
private static const char[] BYE = "bye";

mixin PropertiesMixin;

public void setSpeech(char[] speech)
{
set(BYE, speech);
}

public char[] speak()
{
return get(BYE);
}
}

I've mixed in the PropertiesMixin for both of them, and then implemented the Speaker interface functions using properties. We could even have a default speech very easily, if needed.

The composite classes are very simple as they do not contain any functionality whatsoever.

public class HelloSpeaker : Speaker
{
mixin HelloMixin;
}

public class ByeSpeaker : Speaker
{
mixin ByeMixin;
}

I've put the setting of the speeches in the main function of the program, where the composites are created, to better demonstrate the usage of the Speaker interface.

public void main()
{
Speaker hello = new HelloSpeaker();
hello.setSpeech("Hello, World!");
Stdout(hello.speak()).newline;

Speaker bye = new ByeSpeaker();
bye.setSpeech("Bye, World!");
Stdout(bye.speak()).newline;
}

I'm pretty impressed about how easy it is to implement complex objects using mixins. Next I need to look into a more complicated version. Something like Fredrik Kalseth has done here.

Until then you can find the new code here.

Composite Oriented Programming

Ok, so I found a new buzzword; Composite Oriented Programming. I first heard it from Thomas Biskup. He wrote about it on his JADE-blog, here. From there I started reading about COP on qi4j.org. I haven't completely understood it, but so far it seems to be quite nice buzzword.

So I wrote a small hello world sample in D using templates. Got it working after a while with the help of downs and Bill Baxter, on #D and D.learn respectively.

You can find the code here.

I'll go over it here now.

First we have a template to hold out data, namely helloStr:

public template HelloData
{
private char[] helloStr;
}

Next we define the action or function for the whole composite. I don't remember what this is in COP-speak.

public interface HelloSpeech
{
public char[] sayHello();
}

Then we implement the above function in HelloSpeaker.

public template HelloSpeaker
{
mixin HelloData;

public char[] sayHello()
{
return helloStr;
}
}

So we mixin the data template, so we can return the helloStr from sayHello. Notice that we don't need to import the actual interface for sayHello.

Then we will create the actual composite.

public class HelloWorld : HelloSpeech
{
mixin HelloSpeaker;

public this()
{
helloStr = "Hello, World!";
}
}

Now we have a concrete class that implements HelloSpeech. If you look at the code here, you notice that we've had to import the HelloData module. This is as far as I can see the only downside so far.

We have now nicely removed the implementation for sayHello to their own modules. This is a very small example, but I can see it to be quite useful with bigger systems and with more interfaces.

Now just add an interface for setting the value for helloStr and creating an implementation for it, then this would be complete. The helloStr could be hidden completely behind a getter/setter-pair.