Learn to model objects and behaviors with Go series: An example with dish washing (Part 1)

Vu Xuan Nhat Tran
The Startup
Published in
5 min readJun 28, 2020
Photo by Marek Studzinski on Unsplash

This is a series of story for programmers who want to start object and behavior modelling with Go. I wish you have a great journey.

Why it is about dish washing???

My girlfriend is really good at cooking, and in contrast, I’m only good at washing dishes, and of course hand washing. Spending time after the meal to wash dishes, pans and bowls makes me feel relax.

It’s not only the work of hands, but my brain also tries to work out the most effective solution for washing.

While you may not agree with that statement, please follow my story to see how I turn the dish washing in to an object and behavior modelling, with the help of my favorite language, Go.

Prerequisites

Unfortunately, this story will require you to have some basic knowledge of Go:

It won’t take you more than 5 minutes to go through these concepts, which is equal to the time I rest on the dinner table before going to wash the dishes.

The simplest model

  1. Objects and behaviors
  • Dish object: encapsulates attributes of the dish:
    - The dirty state of the dish.
    Dish should have the behaviors:
    - Clean itself.
  • Sponge object: encapsulates attributes of the sponge (you can not wash without sponge!!):
    - Color of the sponge.
    - Number of times we can use sponge to wash until no more liquid.
    Sponge
    should have the behaviors:
    - Wash the dish.
    - Top up liquid.
  • LiquidDispenser object: one of my housemate does not want to use dish washing liquid, and I told him to stay away from the sink. It has attributes:
    - The amount of liquid it stores.
    LiquidDispenser should have the behaviors:
    - Shows the amount of liquid left.
    - Dispenses an amount of liquid.

2. Implementation using Go

dish.go

dish.go

sponge.go

sponge.go

liquiddispenser.go

liquiddispenser.go

For people who are not familiar with Go, let me explain the syntax.

Constant

Sponge has maximum of the times it can be used to wash with liquid topped up. And it’s naturally to store it as a constant.

Struct

In Go, we use struct as the class for the object we want to model.

struct can have fields (attributes) or methods (member functions)

Go does have the concept for exported and non-exported identifiers which is similar to private and public in some way.

From https://golang.org/ref/spec#Exported_identifiersAn identifier may be exported to permit access to it from another package. An identifier is exported if both:- the first character of the identifier's name is a Unicode upper case  letter (Unicode class "Lu")- the identifier is declared in the package block or it is a field name or  method name.All other identifiers are not exported.

Example from our dish washing code:

  • Non-capital letter amount specifies that amount is the non-exported attribute.
  • Capital letter Color specifies that Coloris exported attribute.
  • Non-capital letter clean specifies the clean() is the non-exported method.
  • Capital letter IsClean specifies that IsClean() is the exported-method.
  • Non-capital letter spongeMaxLiquid specifies that spongeMaxLiquid is the non-exported constant.
  • Captial letter Yellow specifies that Yellow is the exported constant.

Pointer vs Value receivers, Pointer vs Value methods

To describe a behavior of a model, or a type in Go, we use methods.

From dish.go example, we can see there are two types of receiver, Dish (value receiver) and *Dish (pointer receiver)

  • The methods that we can call with receiver Dish are methods declare with type Dish (IsClean() )
  • The methods that we can call with receiver *Dish are methods declare with type Dish and type *Dish (IsClean() and clean() )

What is the different from pointer and value receiver?

  • Value methods can be invoked on pointer and value receivers, but pointer methods can only be invoked on pointer receivers.
  • Pointer methods can modify the receiver, while invoking value methods would cause the method to receive a copy of the value, so any modifications would be discarded.

So we will use value methods for read-only or non-modification behavior, and use pointer methods for modification behavior.

  • IsClean() only displays the clean state of the dish.
  • clean() modifies the internal state dirty of the dish.

Hence, when designing model (type) that will require internal state modification, we usually create and use pointer receiver even for value methods. It’s help to signal the user of our model not to create value type object.

The main function

Go program’s entry point is the main() function

Now we can run the program

// in the package dishwashing_part1/
$ go build -o main
$ ./main
Before wash, is dish 1 clean? = false
After wash, is dish 1 clean? = true
Before wash, is dish 2 clean? = false
After wash, is dish 2 clean? = true
Before wash, is dish 3 clean? = false
After wash, is dish 3 clean? = true
Before wash, is dish 4 clean? = false
After wash, is dish 4 clean? = true
Before wash, is dish 5 clean? = false
After wash, is dish 5 clean? = true
Before wash, is dish 6 clean? = false
After wash, is dish 6 clean? = true
Before wash, is dish 7 clean? = false
After wash, is dish 7 clean? = true
Before wash, is dish 8 clean? = false
After wash, is dish 8 clean? = true
Before wash, is dish 9 clean? = false
After wash, is dish 9 clean? = true
Before wash, is dish 10 clean? = false
After wash, is dish 10 clean? = true

So it is, a simple Go program that models the operation of washing dishes.

You can find the source code here

Let’s recall our purpose: object and behavior modelling! And the model is the simplest model we can think about.

Although the program seems to do well, there are a lot of improvements can be done on the models and their behaviors. We will discuss more on part 2 of this series.

Thanks for reading!

--

--