📜 ⬆️ ⬇️

Object-disoriented language


Every time when it comes to Go you have to hear the same question:
Is Go an object-oriented language?

Honestly, it finally got me. My task is to paint this topic in this article, print a link on business cards and distribute them every time PLO 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 like objects and even seem to provide a model of inheritance, 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 would call inheritance. Let's say for now that Woman inherits from Person . Type Woman contains all fields of type Person , can reassign its functions, can call methods of the parent type. As mentioned above, at first glance it looks like inheritance. Yeah ... but this is just a trick!
')

Trick?


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

author's picture

Now imagine the structure as 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 big box and put a small box inside it along with other items. For example:

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

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

We can respond in different ways. On the one hand, we can briefly say that there are books, toys and some small box in it, but on the other, we can be more detailed in saying that the box contains toys, books, baseball cards and some kind of magic items. Both answers are correct, 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 a relative of 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. Again, you need to forget this term. This magic trick is nothing more than a call to a function belonging to a box inside a large box that takes place outside, such as here:

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

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

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

However, functions can be explicitly invoked 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 SmallBox .

We are greedy 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 BigBox may or may not contain SmallBox inside. Until now, we constantly allocated memory for a small box, although it was not used. We can do the same thing a little more efficiently by incorporating a pointer into our structure:

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

But there is one trick here, 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 little box in absolutely the same way as any other pointer:

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

Hooray! Now everything works fine! 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 here. The so-called inheritance is nothing more than a special type of field that provides shortcuts to eigenfunctions. Simple, smart and enough to answer OOP fanatics:
Of course, this is OOP ... Forward!

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)

Source: https://habr.com/ru/post/167247/


All Articles