Today I’d like to talk about design patterns, and one of them in particular. As announced in a preview post, Go language introduction, I will implement Composite in Go, the new programming language from google. Introduced by Erich Gamma, Richard Helm, Ralph Johnson, John M. Vlissides in their software designer bible: Design Patterns: Elements of Reusable Object-Oriented Software. I will implement one of these pattern in Go language, to exploring new features of this language and how they allow to use high level paradigm using a low level language.
Design patterns are a useful way to manage tipical problems emerging during develpment process. As said, I want to pay special attention to one of these patterns: Composite. It is not easy to explain whatg Composite is, so go on with a case study: imagine a system that send and receive packets in some stream. During the design process, we identify two important entity: Packet and Message. Messages are composed by Packet, each Packet has some data, and so on. But there is a special requirement: to meet the needs of the underlayng layers, the system should be able to group many Message objects to create other, bigger Messages. To make a long story short, sometimes Messages (group of Packets) act like single Packet, to be group again and so on. Hope it is clear.
Using Composite, we don’t need to consider Packets and Messages as different things: both of them should implement the same interface, PacketComposite, to define a Message as a collection of PacketComposite. So Messages and Packets will both implements the 2 needed methods: print() and addChild(). The first one will print the data if called from a Packet, the entire collection if called from Message. That’s what we call polymorfism in OO. The addChild() method will have an empty implementation for Packet, since we don’t need to add Packets to Packets. So, let’s take a look to the data structures needed:
package main
import "fmt"
type PacketComposite interface {
print()
addChild(p PacketComposite, i int)
}
type Packet struct {
value int
}
type Message struct {
chain [3]PacketComposite
dim int
label int
}
Ok, we declared what we need, fmt is a Go package that I will use to printf messages. Message contains a collection of PacketComposite, but we need a Packet/Message collection in it. There are no explicit relationships among these classes yet, so at this point we need to declare Packet and Message to be implementation of PacketComposite. Here the good news: in google’s go, it can’t be easy.
We just need to implement the methods print() and addChild() for Packet and Message, and they will automatically considered as PacketComposite implementation. That’s it, these classes are not declared to be an implementation of the interface, but they become that if we implement both the methods required by the interface. Let’s do it:
func (p Packet) print() {
var n int
n = p.value
fmt.Printf("packet value: %d\n", n)
}
func (pa Packet) addChild(p PacketComposite, i int) {
fmt.Printf("Nothing to add")
}
func (m *Message) print() {
var i int
fmt.Printf("\nMessage%d\n", m.label)
if m.dim == 0 {
return
}
for i=0; i
As announced, we leave the addChild() method empty for Packet. The implementation for Message is not the best I can do, you have to pass an integer to use as index for the array that implement the collection, of course we can do better, but keep concentrated on the topic: implement Composite in Go. At this point, we have all we need to speack about Composite. We can also define other methods for Packet and Message:
func (p Packet) set(i int) {
p.value = i
}
// Equivalent to constructor
func newMessage(i int) *Message {
return &Message{dim: 0, label: i}
}
func newPacket(i int) *Packet {
return &Packet{value: i}
}
We implement set() method for Packet and two functions newMessage and NewPacket that will work as constructors (one really interesting features in Go is the {} notation for inizializing an object, simply pass the value in a name-value couple, we will talk in detail about it in a next post). So lets complete everything with a main example:
func main() {
var m, m1 PacketComposite
var p1, p2 PacketComposite
p1 = newPacket(1)
p2 = newPacket(2)
m = newMessage(0)
m1 = newMessage(1)
m1.addChild(p1, 0)
m1.addChild(p2, 1)
m.addChild(p1, 0)
m.addChild(m1, 1)
m.print()
}
we declare m, m1, p1 and p2 as PacketComposite, and than we use the init function we defined to create Packet and Message. We can add p1 and p2 to m1 since they are declared to be PacketComposite, and we can add m1 to m for the same reason: they are both PacketComposite.
Here the output of the program, after been compiled using 8g composite.go and linked using 8l composite.8. You can found complete compiling and linking instruction for each allowed system in the official website golang.org. I indent manually the output text because the code to indent is out of topic and to keep the source clean
Message0
packet value: 1
Message1
packet value: 1
packet value: 2
In next posts, I will talk about initialization in Go, exploring new features of google's new programming language and to understand if they are good or not.