Object-disoriented language

Original author: Krzysiek
  • Transfer

Every time it comes to Go, you have to hear the same question:
Is Go an Object Oriented Language?

Honestly, it completely bored me. My task is to paint this very topic in this article, print a link on business cards and give them out every time OOP fans ask me this question.

Structures at a glance


author's picture

In short, there is no strict answer to this sacred question. Go structures at first glance work as objects and seem to even provide an inheritance model, for example:

type Person struct {
        Name string
}
func (p *Person) Intro() string {
        return p.Name
}
type Woman struct {
        Person
}
func (w *Woman) Intro() string {
        return "Mrs. " + w.Person.Intro()
}

The example above shows the simplest example of what most of you will call inheritance. Let's just say that Woman inherits from Person . The Woman type contains all fields of the Person type , can reassign its functions, can call methods of the parent type. As mentioned above, at first glance it looks like inheritance. Yeah ... but it's just a trick!

Trick?


Let's take a closer look, and let me explain how this works inside. First things first, there is no real inheritance. It would be great if you forgot everything you know about inheritance while reading all this ... Chick!

author's picture

Now imagine the structure in the form of a box. An ordinary gray cardboard box ... And imagine the fields as things, some magic items that you can put inside the box. Here is an example:

type SmallBox struct {
        BaseballCards   []string
        BubbleGumsCount int
        AnyMagicItem    bool
}

Yeah, that’s how our little box might look. But one day it may not be enough, right? Or maybe you want to share your things. In this case, you can take a large box and put a small box inside it along with other items. For instance:

type BigBox struct {
        SmallBox
        Books []string
        Toys  []string
}

Great, we have a large box containing all the items available in a small plus a few books and toys. And here the magic happens ... We can ask:
What is in the big box?

We can answer in different ways. On the one hand, we can briefly say that it contains books, toys and some kind of small box, but on the other, we can be more detailed by saying that the box contains toys, books, baseball cards and some kind of magic items. Both answers are true, but different in their details. Go, among other things, allows you to determine the level of this detail, for example:

bigBox := &BigBox{}
bigBox.BubbleGumsCount = 4          // correct...
bigBox.SmallBox.AnyMagicItem = true // also correct

In short, BigBox is not related to SmallBox , it only contains one instance of SmallBox inside and provides quick access to its fields.

Redefinition?


In the very first example, you saw something like a method override. I repeat again, you need to forget this term. This magic trick is nothing more than a call to the function belonging to the box located inside the outside of the large box, as for example here:

func (sb *SmallBox) Capacity() int {
        return 20
}
func (bb *BigBox) Capacity() int {
        return bb.SmallBox.Capacity() * 3
}

We specify that BigBox can contain three times as many items as a small box, but we do not redefine the function belonging to SmallBox . We can still access both of them. they belong to different boxes.

fmt.Println(bigBox.Capacity())          // => 60
fmt.Println(bigBox.SmallBox.Capacity()) // => 20

However, functions can be explicitly called from an external box using abbreviations:

func (sb *SmallBox) Color() string {
        return "gray"
}
// *snip*
bigBox.SmallBox.Color() // => "gray"
bigBox.Color()          // => "gray"

This is a killer feature that brings a breath of fresh air to Go in the matter of inheritance. The Color function in both calls refers to the same function associated with the SmallBox .

We are greedy for memory!


Go is generally a system programming language and allows us to manage memory using pointers to some extent. We can use them to save memory when working with structures. It can be assumed that a BigBox may or may not contain a SmallBox within itself . Until now, we constantly allocated memory for a small box, although it was not used. We can do the same a little more efficiently by including a pointer in our structure:

type SkinflintBigBox struct {
        *SmallBox
        Books []string
        Toys  []string
}

But there is one trick, this included structure works in the same way as any other pointer , so we need to remember that it needs to be initialized , otherwise a lot of bad things can happen:

bigBox := &SkinflintBigBox{}
bigBox.SmallBox     // => nil
bigBox.AnyMagicItem // ...

author's picture

We need to initialize our small box in the same way as any other pointer:

bigBox := &SkinflintBigBox{SmallBox: &SmallBox{AnyMagicItem: true}}
bigBox.AnyMagicItem // => true

Hurrah! Now everything works great! In addition, you may be interested to know that the included pointer can be initialized at any time , it is not necessary to do this when initializing the external structure.

This is not magic, this is a trick ...


Summing up, there is no magic. So-called inheritance is nothing more than a special type of field that provides abbreviations for its own functions. Simple, smart and enough to answer OOP fanatics:
Of course, this is OOP ... Go!

author's picture

That's all, I hope you enjoyed it!

( note: according to the requirements of the public, the author’s pictures are hidden behind the links)

Also popular now: