Channels ▼
RSS

Open Source

Go Introduction: How Go Handles Objects


This article is the third in our five-week overview of the Go language. The first installment explained how Go is unusual and explained how to download it and set up the envirnoment for generating binaries. The second article examined various apsects of the language. In this article, the focus moves to another innovative aspect of Go: its approach to object-oriented programming. In this article, I refer to material covered in the first two installments, so make sure to have read or at least scanned them.

To an object-oriented purist, neither C++ nor Java are object-oriented because those languages use method calls rather than message passing. Pragmatists equally might question whether Go is object-oriented, since in addition to using methods rather than messages, it doesn't support inheritance. This apparent lack is a deliberate design choice — which reflects a growing discontent with deep, complex object hierarchies that can be very inflexible to use and expensive to change. Many programmers are switching to using aggregation wherever possible, and Go provides particular support for this approach.

This article's first part provides a very short introduction to some of the basics of object-oriented Go programming. The second part compares how someone coming from C++ or Java might implement some classes and use inheritance to add additional functionality (but using Go syntax, and using embedding to simulate inheritance), in contrast to the approach a Go programmer would take.

Note that although Go is a small and young language, it is quite sophisticated, so this short article can only give a brief taste of its capabilities. Furthermore, the syntax for the basic control structures (such as if and for statements) isn't explained, since it should be straightforward to infer despite the differences from other languages.

Interfaces for Duck Typing

To create a custom type in Go we use the type keyword. For example:

type Widget struct {
    X, Y int
}
type Label struct {
    Widget        // Embedding (delegation)
    Text   string // Aggregation
}

Go types don't need to be based on a struct although they often are. A Go struct is more like a C struct than a C++ struct because it is not a class. (As we will see, though, methods can be added to any custom Go type, including structs.) The Widget aggregates two public int fields (public fields are indicated by a leading uppercase letter; private ones by a leading lowercase letter). The Label aggregates a string (an immutable sequence of normally UTF-8-encoded bytes), and embeds a Widget. This means that we can create a Label value— the terms "object" and "instance" are not used in Go — like this:

label := Label{Widget{10, 10}, "State:"}

We can then access its aggregated and embedded fields by their names, that is, label.Text or label.X. Of course, if we added an X field to the struct, we would then need to disambiguate: label.X (the public X field), label.Widget.X (the public embedded Widget's public X field).

Any custom type can have methods added to it. Here is an example of a method for the Label type, along with the output produced by calling the method on a Label value (so, label.Paint()).

func (label Label) Paint() {
    fmt.Printf("%p:Label.Paint(%q)\n", &label, label.Text)
}

------------------------------------------------------------------
0xf840033180:Label.Paint("State:")

Rather than using this or self, Go methods typically use sensible custom-type-specific names for their "receiver" (that is, for the value on which they are called). We have followed that convention by calling the receiver label, although core Go developers would have just called it l. This particular method takes no arguments and returns nothing; we will see more interesting methods further on.

Incidentally, the "%p" format specifier says to output a pointer address (for example, for debugging); and the "%q" specifier says to output a string in quotes.

type Button struct {
    Label // Embedding (delegation)
}

func NewButton(x, y int, text string) Button {
    return Button{Label{Widget{x, y}, text}}
}
func (button Button) Paint() { // Override
    fmt.Printf("%p:Button.Paint(%q)\n", &button, button.Text)
}

func (button Button) Click() {
    fmt.Printf("%p:Button.Click()\n", &button)
}

Here, we have defined a Button type — really just a clickable Label, although it has its own Paint() method. If Button didn't have its own Paint() method, calling button.Paint() would be almost the same as calling button.Label.Paint() — however, in the latter case, the receiver passed to the method would be the Label field, not the whole Button.

We've also added a convenience construction function (Go does not have constructors), so that we can create Buttons using a nicer syntax than we used for the label we created earlier. Here are two ways to create buttons for comparison.

button1 := Button{Label{Widget{10, 70}, "OK"}}
button2 := NewButton(50, 70, "Cancel")

Unlike some other languages, Go always initializes every variable, field, and so on, to its zero value (empty string, 0 int, 0.0 float64, nil pointers and references, etc.); but when this isn't suitable, we can easily create a "New" function to create a suitably initialized value.

Clearly, embedding (often called "delegation" in other contexts) provides a kind of inheritance for Go types. For C++ and Java programmers, this seems like a very natural way to move into Go programming — but be aware that the Go developers advocate avoiding inheritance altogether; we will see how shortly.

Go Interfaces

For now, let's continue a little further with this example to show another aspect of Go programming: the use of interfaces. Suppose we have a bunch of widget values of different types, Buttons, Labels, ListBoxes, and so on. All these might be in a layout and need painting. Here's how we can do this using Go interfaces and duck typing.

 type Painter interface {
    Paint()
}

First, we create an interface; this is a type in its own right, but wholly abstract. It isn't possible as such to create a value of an interface type. However, we can create variables that hold interface values — such variables must be assigned the value of a concrete type that satisfies the interface; that is, it has the method or methods that the interface specifies.

In this example, the Button, Label, and ListBox (not shown) types all have a Paint() method that matches the signature for the Painter interface, so all these types are Painters — even though there is no explicit connection between the concrete types and the interface! This allows us to write code like this:

for _, painter := range []Painter{label, listBox, button1, button2} {
    painter.Paint()
}

This is pure — and type-safe— duck typing: Go doesn't care what the concrete types of label, listBox, button1, and button2 are. All that matters to Go (and this is enforced by the compiler), is that the given values satisfy the Painter interface (that is, have a Paint() method that takes no arguments and returns nothing). What happens if we want to apply an operation to a bunch of values — but only to those values that support the operation? Again, we begin by creating a suitable interface.

 type Clicker interface {
    Click()
}

Any custom type, no matter what it is, that has a Click() method satisfies the Clicker interface. In this case, the Label is not a Clicker, but the Button and ListBox are.

for _, widget := range []interface{}{label, listBox, button1, button2} {
    if clicker, ok := widget.(Clicker); ok {
	clicker.Click()
    }
}

Here, we iterate over a slice of interface{} values. These are values of any built-in or custom type (rather like Java's Object or a C++ void*) that knows its own type, and use a checked type assertion to determine whether the value has a Click() method. Again, Go doesn't care what the value's actual type is, only whether it has the required method.

Aggregation vs. Inheritance

Now, let's create a couple of types (one "inheriting" from the other) in the conventional object-oriented way, but using Go syntax. We will then highlight a major downside of the conventional approach, show how to avoid it using the Strategy pattern, and how to implement this in Go.

Here is a very simple (and space-inefficient) way of creating a custom set type based on Go's built-in map type, which stores key-value pairs and where every key is unique.

type IntSet struct {
    data map[int]bool
}

func NewIntSet() IntSet {
    return IntSet{make(map[int]bool)}
}
func (set *IntSet) Add(x int) {
    set.data[x] = true
}
func (set *IntSet) Delete(x int) {
    delete(set.data, x)
}
func (set *IntSet) Contains(x int) bool {
    return set.data[x]
}

The data map's keys are ints and its values are bools. Go maps must be initialized using the built-in make() function: This returns a reference to a ready-to-use empty map. Since map keys are unique, if a duplicate key-value pair is added in the Add() method, the value is replaced. Similarly, the delete() method removes the given key-value pair from the map, or harmlessly does nothing if the key isn't in the map. (Go doesn't need a delete or free function for freeing memory because it has a garbage collector.)


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 

Video