What is an Abstract Class?

An Abstract Class is the layout of the class. It defines several methods, some concrete and some abstract. The child class extending the abstract class needs to implement the abstract methods. It can also override the implementation of a concrete method.

Why use Abstract Class?

An abstract class is used when we want to provide a common interface for different implementations. But those multiple implementations have some methods in common which can be defined in the base class itself and used by all.

Consider the following use case: Let’s say we are coding for a barista and need to write code for creating coffee and tea. We can have a Beverage abstract class which can implement the common methods: boilWater() and pourInCup(). Whereas the concrete classes: Tea and Coffee can implement their own prepareRecipe() method. usecase

Why is it difficult in Go?

Though Go provides a custom type known as interface which can be used to provide a common interface for different implementations. But Go’s interface doesn’t have fields and also it doesn’t allow the definition of methods inside it. Any type needs to implement all methods of interface to become of that interface type.

Solution

We will make use of the embedded field type in a struct.

Let’s first define the beverage interface.

type beverage interface {
	boilWater()
	prepareRecipe()
	pourInCup()
}

Next we define a struct - baseBeverage which will implement the common methods of the beverage interface.

type baseBeverage struct {}

func (b baseBeverage) boilWater() {
	fmt.Println("Boiling water")
}

func (b baseBeverage) pourInCup() {
	fmt.Println("Pouring in beverage in cup")
}

Now we define our concrete beverages, i.e, tea and coffee. These structs will contain baseBeverage as an embedded field and also implement the methods which is related to them.

type tea struct {
	baseBeverage
}

func (t tea) prepareRecipe() {
	fmt.Println("Putting tea leaves")
}

type coffee struct {
	baseBeverage
}

func (c coffee) prepareRecipe() {
	fmt.Println("Putting coffee")
}

When struct A contains another struct B as an embedded field, the method sets of S include the promoted methods with receiver B. Hence, when tea embeds baseBeverage, all the methods of baseBeverage can be used by objects of tea as well.

func main() {
	t := tea{}
	t.boilWater()
	t.prepareRecipe()
	t.pourInCup()
}

This allows us to use an object of struct tea as a beverage interface type.