Is Go an object-oriented programming language?

This article was first published from "MOOC". If you want to know more about IT dry goods and hot news in the programmer circle, welcome to pay attention to "MOOC"!

Author: tonybai | MOOC lecturer

The Go language has been open source for 13 years . In the recent programming language rankings released by TIOBE in March 2023, Go broke into the top ten again, and compared with Go's ranking at the end of 2022 , it has improved by 2 places:

The "prediction" that Go has started to fly in the past two years is gradually becoming a reality_, and everyone's enthusiasm for learning Go is also rapidly increasing. Many readers are coming into contact with Go for the first time. Many of you come from OO (object-oriented) language camps like Java and Ruby. The first question many children’s shoes ask after learning Go is: Is Go an OO language ? is it ? In this blog post, let’s explore that.

1. Traceability

In the book "Go Programming Language", the recognized "Bible" of Go language , there is such a kinship diagram of Go language and its main ancestor programming language:

From the figure, we can clearly see the "inheritance context" of the Go language:

We have seen that Go did not learn from pure object-oriented languages ​​such as Simula, SmallTalk, etc. from the traceability of Go's ancestors.

Go was born in 2007 and open source in 2009, which was the period when object-oriented languages ​​and OO paradigms were popular. However, Go designers feel that the classic OO inheritance system does not seem to be of much benefit to program design and expansion, and it also brings more restrictions. Therefore, the official version does not support the classic OO syntax, that is, class-based The three mainstream OO features of encapsulation, inheritance and polymorphism implemented by objects.

But does this mean that Go is not an OO language? No! Object Oberon with object-oriented mechanism is also one of the ancestor languages ​​of Go, although the OO syntax of Object Oberon is quite different from our common syntax today.

Regarding this issue, I also specifically consulted ChatGPT_ , and the answer I got is as follows:

ChatGPT believes that Go supports object-oriented and provides support for the basic concepts of the object-oriented paradigm, but the means of support are not classes and objects.

So does Go officially respond to this question? Yes, let's take a look.

2. Official voice

Go official made a brief response to whether Go is an OO language in the FAQ :

Is Go an object-oriented language?

Yes and no. Although Go has types and methods and allows an object-oriented style of programming, there is no type hierarchy. The concept of “interface” in Go provides a different approach that we believe is easy to use and in some ways more general. There are also ways to embed types in other types to provide something analogous—but not identical—to subclassing. Moreover, methods in Go are more general than in C++ or Java: they can be defined for any sort of data, even built-in types such as plain, “unboxed” integers. They are not restricted to structs (classes).

Also, the lack of a type hierarchy makes “objects” in Go feel much more lightweight than in languages such as C++ or Java.

A rough translation is:

Go是一种面向对象的语言吗?

是,也不是。虽然Go有类型和方法,并且允许面向对象的编程风格,但却没有类型层次。Go中的“接口”概念提供了一种不同的OO实现方案,我们认为这种方案更易于使用,而且在某些方面更加通用。还有一些可以将类型嵌入到其他类型中以提供类似子类但又不等同于子类的机制。此外,Go中的方法比C++或Java中的方法更通用:Go可以为任何数据类型定义方法,甚至是内置类型,如普通的、“未装箱的”整数。Go的方法并不局限于结构体(类)。

此外,由于去掉了类型层次,Go中的“对象”比C++或Java等语言更轻巧。

"Yes and no"! We see that the Go official gave a moderate answer of "no harm to both parties". So what does the Go community think? Let's take a look at the views of some typical representatives of the Go community.

3. Community Voices

Both Jaana Dogan and Steve Francia are former members of the Go core team. Before joining the Go team, they also had their own views on the issue of "Whether Go is an OO language".

Jaana Dogan 's point of view in the article "The Go type system for newcomers" is: Go is considered as an object-oriented language even though it lacks type hierarchy , that is, "Go is considered an object-oriented language even though it lacks type hierarchy Missing type hierarchy".

And earlier is the conclusion of Steve Francia's article "Is Go an Object Oriented language?" published in 2014: Go, object-oriented programming without objects or inheritance , can also be called "object-free" OO programming Model.

The wording of the two expressions is different, but the meaning is the same, that is, Go supports object-oriented programming, but it does not achieve it by providing classic classes, objects, and type hierarchies .

So how does Go implement support for OOP? Let's keep watching!

4. Go's "objectless" OO programming

The three major features of classic OO are encapsulation, inheritance, and polymorphism. Here we see how they correspond in Go.

1. Packaging

Encapsulation is to "package" data and methods of manipulating data into an abstract data type. This type of encapsulation hides the implementation details, and all data can only be accessed and manipulated through exported methods. Instances of this abstract data type are called objects . Classical OO languages, such as Java and C++, express the concept of encapsulation through classes, and map objects through class instances. Children's shoes who are familiar with Java must remember the title of the second chapter of "Java Programming Thoughts" : "Everything is an object". All properties and methods in Java are defined in each class.

The Go language does not have classes, so how does the concept of encapsulation manifest itself? After entering the Go world, beginners from the OO language like to "check their seats", that is, which grammatical element in Go is closest to class! So they found the struct type.

The struct type in Go provides the ability to abstract real-world aggregates. The definition of struct can contain a set of fields (fields). From an OO perspective, you can also treat these fields as attributes. At the same time, we also Methods can be defined for struct types. In the following example, we define a struct type named Point, which has an exported method Length:

type Point struct {
    x, y float64
}

func (p Point) Length() float64 {
    return math.Sqrt(p.x * p.x + p.y * p.y)
}

We see that, from a grammatical point of view, unlike the classic OO declaration class method, the Go method declaration does not need to be placed in the curly braces that declare the struct type. The link between the Length method and the Point type is a syntax element called the receiver parameter .

So, does struct correspond to the class in classic OO? Yes and no! From the perspective of data aggregation abstraction, it seems that the struct type can have multiple heterogeneous fields representing different abstract capabilities (for example, the integer type int can be used to abstract the length of a real-world object, and the string type field can be used to names of abstract real-world objects, etc.).

But from the perspective of having methods, not only struct types, but all other named types in Go except built-in types can have their own methods , even a new type MyInt whose underlying type is int:

type MyInt int

func(a MyInt)Add(b int) MyInt {
	return a + MyInt(b)
}

2. Inheritance

As mentioned earlier, Go designers re-evaluated support for classic OO grammatical concepts at the beginning of Go's birth, and finally gave up support for classes, objects, and class inheritance hierarchies. That is to say: the types that embody the concept of encapsulation in Go are all "passers-by", and there is no "fetter" of the relationship between a father and a son .

When it comes to inheritance in OO, what everyone thinks more is that the subclass inherits the properties and method implementation of the parent class. Although Go does not have an explicit inheritance syntax like the Java extends keyword, Go also provides support for "inheritance" in another way. This type of support is type embedding, see an example:

type P struct {
	A int
	b string
}

func (P) M1() {
}

func (P) M2() {
}

type Q struct {
	c [5]int
	D float64
}

func (Q) M3() {
}

func (Q) M4() {
}

type T struct {
	P
    Q
    E int
}

func main() {
	var t T
    t.M1()
    t.M2()
    t.M3()
    t.M4()
	println(t.A, t.D, t.E)
}

We see that type T "inherits" the derived methods (M1~M4) and derived fields (A, D) of P and Q by embedding two types of P and Q.

However, the actual "inheritance" mechanism in Go is not the inheritance in classic OO, and there is no "kinship" relationship between the peripheral type (T) and the embedded type (P, Q). The exported fields and methods of P and Q are just promoted to the fields and methods of T, which is essentially a combination and an implementation of the delegate mode in the combination. T is just a proxy (delegate), and it provides all the methods it can delegate, such as the M1~M4 methods in the example. When the outside world initiates a call to the M1 method of T, T delegates the call to its internal P instance to actually execute the M1 method.

To understand with the classic OO theory, the relationship between T, P and Q is not is-a, but has-a .

3. Polymorphism

Polymorphism in classic OO refers especially to runtime polymorphism , which means that when a method is called, different types of method implementations will be called according to the type of the actual object that calls the method.

The following is an example of typical polymorphism in C++:

#include <iostream>

class P {
		public:
				virtual void M() = 0;
};

class C1: public P {
		public:
				void M();
};

void C1::M() {
		std::cout << "c1.M()\n";
}

class C2: public P {
		public:
				void M();
};

void C2::M() {
		std::cout << "c2.M()\n";
}

int main() {
		C1 c1;
		C2 c2;
		P *p = &c1;
		p->M(); // c1.M()
		p = &c2;
		p->M(); // c2.M()
}

This code is relatively clear, a parent class P and two subclasses C1 and C2. The parent class P has a virtual member function M, and the two subclasses C1 and C2 rewrite the M member function respectively. In main, we declare the pointer of the parent class P, and then assign the object instances of C1 and C2 to p and call the member function of M. From the result, the function actually called by p at runtime will be based on the object instance it points to The actual types of C1 and C2 are called M respectively.

Obviously, the polymorphic implementation of classic OO relies on the hierarchical relationship of types. So for Go, which has no type hierarchy, how does it achieve polymorphism? Go uses interfaces to unlock polymorphism !

Compared with classical OO languages, Go places more emphasis on behavior aggregation and consistency rather than data. Therefore, Go provides support for duck typing, that is, type adaptation based on behavior sets. However, compared with dynamic languages ​​such as ruby, Go's static type mechanism can also guarantee type safety when duck typing is applied.

Go's interface type is essentially a set of method collections (behavior collections). If a type implements all the methods in an interface type, it can be assigned to the interface type as a dynamic type. Calling a method through the interface type variable actually calls the method implementation of its dynamic type. See the following example:

type MyInterface interface {
	M1()
	M2()
	M3()
}

type P struct {
}

func (P) M1() {}
func (P) M2() {}
func (P) M3() {}

type Q int 
func (Q) M1() {}
func (Q) M2() {}
func (Q) M3() {}

func main() {
	var p P
	var q Q
	var i MyInterface = p
	i.M1() // P.M1
	i.M2() // P.M2
	i.M3() // P.M3

	i = q
	i.M1() // Q.M1
	i.M2() // Q.M2
	i.M3() // Q.M3
}

Isn't Go's polymorphic implementation that does not require a type inheritance hierarchy and a low-coupling method lighter and easier to use?

Five. Gopher's "OO thinking"

At this point, have the friends from the classic OO language camp found the reason why they "felt awkward" when they started Go language? This "awkwardness" lies in the difference between the way Go supports OO and the classic OO language : the small partners who uphold the classic OO thinking will establish an inheritance hierarchy system when they come up, but Go does not have and does not need it.

It is actually not difficult to transform into the authentic Gopher OO thinking, that is, " prefer interface, prefer combination, change the accustomed is-a thinking to has-a thinking ".

6. Summary

Time to give some concluding points:

  • Go supports OO, but it does not use the classic OO syntax and hierarchical type system;
  • Go supports OO, but it requires a different way of thinking to use it;
  • The way of thinking to play OO in Go is: "priority interface, priority combination".

Welcome to pay attention to the official account of "MOOC". We will always insist on original content, provide high-quality content in the IT circle, and share dry knowledge. Let's grow together!

This article was originally published on Muke.com, please indicate the source for reprinting, thank you for your cooperation

Guess you like

Origin blog.csdn.net/mukewangguanfang/article/details/130105354