Chapter 3. Structural Patterns - Composite, Adapter, and Bridge Design Patterns

We are going to start our journey through the world of structural patterns. Structural patterns, as the name implies, help us to shape our applications with commonly used structures and relationships.

The Go language, by nature, encourages use of composition almost exclusively by its lack of inheritance. Because of this, we have been using the Composite design pattern extensively until now, so let's start by defining the Composite design pattern.

Composite design pattern

The Composite design pattern favors composition (commonly defined as a has a relationship) over inheritance (an is a relationship). The composition over inheritance approach has been a source of discussions among engineers since the nineties. We will learn how to create object structures by using a has a approach. All in all, Go doesn't have inheritance because it doesn't need it!

Description

In the Composite design pattern, you will create hierarchies and trees of objects. Objects have different objects with their own fields and methods inside them. This approach is very powerful and solves many problems of inheritance and multiple inheritances. For example, a typical inheritance problem is when you have an entity that inherits from two completely different classes, which have absolutely no relationship between them. Imagine an athlete who trains, and who is a swimmer who swims:

  • The Athlete class has a Train() method
  • The Swimmer class has a Swim() method

The Swimmer class inherits from the Athlete class, so it inherits its Train method and declares its own Swim method. You could also have a cyclist who is also an athlete, and declares a Ride method.

But now imagine an animal that eats, like a dog that also barks:

  • The Cyclist class has a Ride() method
  • The Animal class has Eat(), Dog(), and Bark() methods

Nothing fancy. You could also have a fish that is an animal, and yes, swims! So, how do you solve it? A fish cannot be a swimmer that also trains. Fish don't train (as far as I know!). You could make a Swimmer interface with a Swim method, and make the swimmer athlete and fish implement it. This would be the best approach, but you still would have to implement swim method twice, so code reusability would be affected. What about a triathlete? They are athletes who swim, run, and ride. With multiple inheritances, you could have a sort of solution, but that will become complex and not maintainable very soon.

Objectives

As you have probably imagined already, the objective of the composition is to avoid this type of hierarchy hell where the complexity of an application could grow too much, and the clarity of the code is affected.

The swimmer and the fish

We will solve the described problem of the athlete and the fish that swims in a very idiomatic Go way. With Go, we can use two types of composition--the direct composition and the embedding composition. We will first solve this problem by using direct composition which is having everything that is needed as fields within the struct.

Requirements and acceptance criteria

Requirements are like the ones described previously. We'll have an athlete and a swimmer. We will also have an animal and a fish. The Swimmer and the Fish methods must share the code. The athlete must train, and the animal must eat:

  • We must have an Athlete struct with a Train method
  • We must have a Swimmer with a Swim method
  • We must have an Animal struct with an Eat method
  • We must have a Fish struct with a Swim method that is shared with the Swimmer, and not have inheritance or hierarchy issues

Creating compositions

The Composite design pattern is a pure structural pattern, and it doesn't have much to test apart from the structure itself. We won't write unit tests in this case, and we'll simply describe the ways to create those compositions in Go.

First, we'll start with the Athlete structure and its Train method:

type Athlete struct{} 
 
func (a *Athlete) Train() { 
  fmt.Println("Training") 
} 

The preceding code is pretty straightforward. Its Train method prints the word Training and a new line. We'll create a composite swimmer that has an Athlete struct inside it:

type CompositeSwimmerA struct{ 
  MyAthlete Athlete 
  MySwim func() 
} 

The CompositeSwimmerA type has a MyAthlete field of type Athlete. It also stores a func() type. Remember that in Go, functions are first-class citizens and they can be used as parameters, fields, or arguments just like any variable. So CompositeSwimmerA has a MySwim field that stores a closure, which takes no arguments and returns nothing. How can I assign a function to it? Well, let's create a function that matches the func() signature (no arguments, no return):

func Swim(){ 
  fmt.Println("Swimming!") 
} 

That's all! The Swim() function takes no arguments and returns nothing, so it can be used as the MySwim field in the CompositeSwimmerA struct:

swimmer := CompositeSwimmerA{ 
  MySwim: Swim, 
} 

swimmer.MyAthlete.Train() 
swimmer.MySwim() 

Because we have a function called Swim(), we can assign it to the MySwim field. Note that the Swim type doesn't have the parenthesis that will execute its contents. This way we take the entire function and copy it to MySwim method.

But wait. We haven't passed any athlete to the MyAthlete field and we are using it! It's going to fail! Let's see what happens when we execute this snippet:

$ go run main.go
Training
Swimming!

That's weird, isn't it? Not really because of the nature of zero-initialization in Go. If you don't pass an Athlete struct to the CompositeSwimmerA type, the compiler will create one with its values zero-initialized, that is, an Athlete struct with its fields initialized to zero. Check out Chapter 1, Ready... Steady... Go! to recall zero-initialization if this seems confusing. Consider the CompositeSwimmerA struct code again:

type CompositeSwimmerA struct{ 
  MyAthlete Athlete 
  MySwim    func() 
} 

Now we have a pointer to a function stored in the MySwim field. We can assign the Swim function the same way, but with an extra step:

localSwim := Swim 
 
swimmer := CompositeSwimmerA{ 
  MySwim: localSwim, 
} 
 
swimmer.MyAthlete.Train() 
swimmer.MySwim () 

First, we need a variable that contains the function Swim. This is because a function doesn't have an address to pass it to the CompositeSwimmerA type. Then, to use this function within the struct, we have to make a two-step call.

What about our fish problem? With our Swim function, it is not a problem anymore. First, we create the Animal struct:

type Animal struct{} 
 
func (r *Animal)Eat() { 
  println("Eating") 
} 

Then we'll create a Shark object that embeds the Animal object:

type Shark struct{ 
  Animal 
  Swim func() 
} 

Wait a second! Where is the field name of the Animal type? Did you realize that I used the word embed in the previous paragraph? This is because, in Go, you can also embed objects within objects to make it look a lot like inheritance. That is, we won't have to explicitly call the field name to have access to its fields and method because they'll be part of us. So the following code will be perfectly okay:

fish := Shark{ 
  Swim: Swim, 
} 
 
fish.Eat() 
fish.Swim() 

Now we have an Animal type, which is zero-initialized and embedded. This is why I can call the Eat method of the Animal struct without creating it or using the intermediate field name. The output of this snippet is the following:

$ go run main.go 
Eating 
Swimming!

Finally, there is a third method to use the Composite pattern. We could create a Swimmer interface with a Swim method and a SwimmerImpl type to embed it in the athlete swimmer:

type Swimmer interface { 
  Swim() 
} 
type Trainer interface { 
  Train() 
} 
 
type SwimmerImpl struct{} 
func (s *SwimmerImpl) Swim(){ 
  println("Swimming!") 
} 
 
type CompositeSwimmerB struct{ 
  Trainer 
  Swimmer 
} 

With this method, you have more explicit control over object creation. The Swimmer field is embedded, but won't be zero-initialized as it is a pointer to an interface. The correct use of this approach will be the following:

swimmer := CompositeSwimmerB{ 
  &Athlete{}, 
  &SwimmerImpl{}, 
} 
 
swimmer.Train() 
swimmer.Swim() 

And the output for CompositeSwimmerB is the following, as expected:

$ go run main.go
Training
Swimming!

Which approach is better? Well, I have a personal preference, which shouldn't be considered the rule of thumb. In my opinion, the interfaces approach is the best for quite a few reasons, but mainly for explicitness. First of all, you are working with interfaces  which are preferred instead of structs. Second, you aren't leaving parts of your code to the zero-initialization feature of the compiler. It's a really powerful feature, but one that must be used with care, because it can lead to runtime problems which you'll find at compile time when working with interfaces. In different situations, zero-initialization will save you at runtime, in fact! But I prefer to work with interfaces as much as possible, so this is not actually one of the options.

Binary Tree compositions

Another very common approach to the Composite pattern is when working with Binary Tree structures. In a Binary Tree, you need to store instances of itself in a field:

type Tree struct { 
  LeafValue int 
  Right     *Tree 
  Left      *Tree 
} 

This is some kind of recursive compositing, and, because of the nature of recursivity, we must use pointers so that the compiler knows how much memory it must reserve for this struct. Our Tree struct stored a LeafValue object for each instance and a new Tree in its Right and Left fields.

With this structure, we could create an object like this:

root := Tree{ 
  LeafValue: 0, 
  Right:&Tree{ 
    LeafValue: 5, 
    Right: &1Tree{ 6, nil, nil }, 
    Left: nil, 
  }, 
  Left:&Tree{ 4, nil, nil }, 
} 

We can print the contents of its deepest branch like this:

fmt.Println(root.Right.Right.LeafValue) 
 
$ go run main.go 
6

Composite pattern versus inheritance

When using the Composite design pattern in Go, you must be very careful not to confuse it with inheritance. For example, when you embed a Parent struct within a Son struct, like in the following example:

type Parent struct { 
  SomeField int 
} 
 
type Son struct { 
  Parent 
} 

You cannot consider that the Son struct is also the Parent struct. What this means is that you cannot pass an instance of the Son struct to a function that is expecting a Parent struct like the following:

func GetParentField(p *Parent) int{ 
  fmt.Println(p.SomeField) 
} 

When you try to pass a Son instance to the GetParentField method, you will get the following error message:

cannot use son (type Son) as type Parent in argument to GetParentField

This, in fact, makes a lot of sense. What's the solution for this? Well, you can simply composite the Son  struct with the parent without embedding so that you can access the Parent instance later:

type Son struct { 
  P Parent 
} 

So now you could use the P field to pass it to the GetParentField method:

son := Son{} 
GetParentField(son.P) 

Final words on the Composite pattern

At this point, you should be really comfortable using the Composite design pattern. It's a very idiomatic Go feature, and the switch from a pure object-oriented language is not very painful. The Composite design pattern makes our structures predictable but also allows us to create most of the design patterns as we will see in later chapters.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.220.179.153