[Go language from entry to actual combat] object-oriented programming

Object-Oriented Programming

Object-oriented programming in Go language is very different from other languages.

image-20230428111344935

Is Go an object-oriented language?

yes and no . While Go has types and methods, and allows an object-oriented programming style, there is no type hierarchy (inheritance). The "interface" concept in Go offers a different approach that we believe is easier to use and in some ways more general. There are also ways to embed types within other types to provide something similar (but not identical) to subclassing. Furthermore, methods in Go are more general than those in C++ or Java: they can be defined for any type of data, even built-in types such as ordinary "unboxed" integers. They are not limited to structures (classes).

Also, the lack of a type hierarchy makes "objects" in Go feel much more lightweight than "objects" in languages ​​like C++ or Java.

Encapsulating Data and Behavior

Structure definition

image-20230428121213526

Instance creation and initialization

image-20230428121307421

type Employee struct {
    
    
	Id   string
	Name string
	Age  int
}

func TestCreateEmployeeObj(t *testing.T) {
    
    
	e := Employee{
    
    "0", "Bob", 20}
	e1 := Employee{
    
    Name: "Mike", Age: 30}
	e2 := new(Employee) // 返回指针
	e2.Id = "2"
	e2.Name = "Rose"
	e2.Age = 22
	t.Log(e)
	t.Log(e1)
	t.Log(e1.Id)
	t.Log(e2)
	t.Logf("e is %T", e)
	t.Logf("e2 is %T", e2)
}

image-20230428122127640

Behavior (method) definition

image-20230428122402465

  • The first

    func (e Employee) String() string {
          
          
    	fmt.Printf("Address is %x", unsafe.Pointer(&e.Name))
    	fmt.Println()
    	return fmt.Sprintf("ID:%s-Name:%s-Age:%d", e.Id, e.Name, e.Age)
    }
    
    func TestStructOperations(t *testing.T) {
          
          
    	e := Employee{
          
          "0", "Bob", 20}
    	fmt.Printf("Address is %x", unsafe.Pointer(&e.Name))
    	fmt.Println()
    	t.Log(e.String())
    }
    

    image-20230428123715323

    So this way of writing will have the overhead of copying.

  • the second

    func (e *Employee) String() string {
          
          
    	fmt.Printf("Address is %x", unsafe.Pointer(&e.Name))
    	fmt.Println()
    	return fmt.Sprintf("ID:%s-Name:%s-Age:%d", e.Id, e.Name, e.Age)
    }
    
    func TestStructOperations(t *testing.T) {
          
          
    	e := &Employee{
          
          "0", "Bob", 20} // 传递引用
    	fmt.Printf("Address is %x", unsafe.Pointer(&e.Name))
    	fmt.Println()
    	t.Log(e.String())
    }
    

    image-20230428123601046

    This is more recommended.

Interface (defines the interaction protocol)

Go's interface is very different from that of many mainstream programming languages.

Example of Java code:

image-20230428124443597

Duck Type interface

Interface of Go language:

image-20230428125301615

type Programmer interface {
    
    
	WriteHelloWorld() string
}

type GoProgrammer struct {
    
    
}

func (g *GoProgrammer) WriteHelloWorld() string {
    
     // duck type 鸭子类型
	return "Hello World"
}

func TestClient(t *testing.T) {
    
    
	var p Programmer
	p = new(GoProgrammer)
	t.Log(p.WriteHelloWorld())
}

image-20230428143644240

Go interface

image-20230428130027398

interface variable

image-20230428130325699

custom type

image-20230428130434903

type IntConv func(op int) int

// 计算函数操作的时长
func timeSpend(inner IntConv) IntConv {
    
     // 以前的方法特别的长 我们可以用自定义类型做替换
	// 类似装饰者模式,对原来的函数进行了一层包装
	return func(n int) int {
    
    
		start := time.Now()
		ret := inner(n)
		fmt.Println("time spend:", time.Since(start).Seconds())
		return ret
	}
}

Extension and reuse (similar to inheritance)

The Go language cannot naturally support inheritance, but it wants to implement object-oriented features.

That is, the parent class object is initialized with the subclass object, then the function called by the parent class object is the function implemented by the subclass, thus satisfying the LSP (subclass exchange principle).

Case 1 : The Go language supports extending the functions of the parent class, as shown in the following code:

package oriented_test

import (
  "fmt"
  "testing"
)

// Pet 类
type Pet struct {
    
    
}

func (p *Pet) Speak(){
    
     // Pet类的函数成员
  	fmt.Print("Pet speak.\n")
}

func (p *Pet) SpeakTo(host string) {
    
     // Pet类的函数成员
  	p.Speak()
  	fmt.Println("Pet SpeakTo ", host)
}

// Dog 扩展Pet的功能
type Dog struct {
    
    
  	p *Pet
}

// 扩展父类的方法
func (d *Dog) Speak(){
    
    
  	d.p.Speak()
}

// 扩展父类的方法
func (d *Dog) SpeakTo(host string) {
    
    
  	d.Speak()
  	fmt.Println("Dog Speakto ", host)
}

func TestDog(t *testing.T) {
    
    
  	dog := new(Dog)
	dog.SpeakTo("Test dog")
}

The output of the above test code is as follows:

image-20230428141543094

Dog's Speak is called in dog's SpeakTo, which calls Pet's Speak, so the output is normal.
Pet and Dog calls do not affect each other, it is entirely up to the user.

But this is different from what we want, the Pet class has its own method, the Dog class has its own method, and the scope of the two is completely different.

Here, the Go language introduces an anonymous nested type , that is, the Dog class does not need to implement its own method with the same name as the Pet class, and the Pet member can be changed in the declaration of the Dog class.

anonymous nested type

Case 2 : Go language supports anonymous function types

// Dog 扩展Pet的功能
type Dog struct {
    
    
  	Pet
}

In this way, Dog does not need to declare its own function member with the same name, and the default call is the call of the Pet member function.

package oriented_test

import (
  	"fmt"
  	"testing"
)

type Pet struct {
    
    
}

func (p *Pet) Speak(){
    
    
  	fmt.Print("Pet speak.\n")
}

func (p *Pet) SpeakTo(host string) {
    
    
  	p.Speak()
  	fmt.Println("Pet SpeakTo ", host)
}

// Dog 扩展Pet的功能
type Dog struct {
    
    
  	Pet // 支持匿名嵌套类型
}

func TestDog(t *testing.T) {
    
    
  	var dog Dog

  	dog.Speak()
  	dog.SpeakTo("Test dog")
}

The final output is as follows:

image-20230428141503654

All member functions of Pet are called, which feels like inheritance, because inheritance defaults to the fact that subclasses can use the public members of the parent class.

Under the anonymous nested type, we want to fully try whether the Go language really supports inheritance. We can implement the same-named function of Pet in Dog like the previous code, and can call the member method of the subclass through the parent class object, like C++ /Java does upcasting like this (impossible by itself, Go language does not support explicit typecasting).

Case 3 : Go language does not support inheritance, the following code:

package oriented_test

import (
  	"fmt"
  	"testing"
)

type Pet struct {
    
    
}

func (p *Pet) Speak(){
    
    
  	fmt.Print("Pet speak.\n")
}

func (p *Pet) SpeakTo(host string) {
    
    
  	p.Speak()
  	fmt.Println("Pet SpeakTo ", host)
}

// Dog 扩展Pet的功能
type Dog struct {
    
    
  	//p *Pet
 	Pet // 支持匿名嵌套类型
}

// 重载父类的方法
func (d *Dog) Speak(){
    
    
  	fmt.Print("Dog speak.\n")
}

// 重载父类的方法
func (d *Dog) SpeakTo(host string) {
    
    
  d.Speak()
  fmt.Println("Dog Speakto ", host)
}

func TestDog(t *testing.T) {
    
    
	var dog Pet = new(Dog) // 这里就会编译错误
	dog.Speak()
	dog.SpeakTo("Test dog")
}
cannot use new(Dog) (value of type *Dog) as Pet value in variable declaration
不支持将Pet类型转换为Dog类型

To sum up, the Go language does not support inheritance, but can support the extension and reuse of interfaces ** (anonymous nested types) **, this method of embedding cannot be used as inheritance at all, because it does not support access to subclasses The method data (overloading) does not support the LSP principle.

Among them, extension means that different classes implement the same member function, which can realize the extended interface form similar to Case 1.

Reuse is to achieve a function similar to overloading through anonymous nested types. You can see the code of Case 2.

polymorphism

image-20230428143317910

type Programmer interface {
    
    
	WriteHelloWorld() string
}

type GoProgrammer struct {
    
    
}

func (g *GoProgrammer) WriteHelloWorld() string {
    
    
	return "fmt.Println(\"Hello World\")"
}

type JavaProgrammer struct {
    
    
}

func (j *JavaProgrammer) WriteHelloWorld() string {
    
    
	return "System.out.println(\"Hello World\")"
}

// 多态
func writeFirstProgram(p Programmer) {
    
    
	fmt.Printf("%T %v\n", p, p.WriteHelloWorld())
}

func TestClient(t *testing.T) {
    
    
	goProgrammer := new(GoProgrammer)
	javaProgrammer := new(JavaProgrammer)
	writeFirstProgram(goProgrammer)
	writeFirstProgram(javaProgrammer)
}

image-20230428144147613

Empty Interfaces and Assertions

image-20230428144326958

func DoSomething(p interface{
    
    }) {
    
    
	// '.' 断言,p.(int),p断言为int类型
	if i, ok := p.(int); ok {
    
    
		fmt.Println("Integer", i)
		return
	}
	if s, ok := p.(string); ok {
    
    
		fmt.Println("string", s)
		return
	}
	fmt.Println("UnKnow type")
}

func TestEmptyInterface(t *testing.T) {
    
    
	DoSomething(10)
	DoSomething("10")
	DoSomething(10.00)
}

image-20230428145558309

Go Interface Best Practices

image-20230428145718339

error mechanism

error

image-20230428150518369

package error

import (
	"errors"
	"testing"
)

func GetFibonacci(n int) ([]int, error) {
    
    
	if n < 2 || n > 100 {
    
    
		return nil, errors.New("n should be in [2, 100]")
	}
	fibList := []int{
    
    1, 1}
	for i := 2; i < n; i++ {
    
    
		fibList = append(fibList, fibList[i-1]+fibList[i-2])
	}
	return fibList, nil
}

func TestGetFibonacci(t *testing.T) {
    
    
	if v, err := GetFibonacci(-10); err != nil {
    
    
		t.Error(err)
	} else {
    
    
		t.Log(v)
	}
}

image-20230428161919642

Best Practices

image-20230428162534768

Fail early and avoid nesting!

example:

func GetFibonacci2(str string) {
    
    
	var (
		i    int
		err  error
		list []int
	)
	if i, err = strconv.Atoi(str); err != nil {
    
    
		fmt.Println("Error", err)
		return
	}
	if list, err = GetFibonacci(i); err != nil {
    
    
		fmt.Println("Error", err)
		return
	}
	fmt.Println(list)
}

panic

image-20230428163230055

panic vs os.Exit

image-20230428163307433

call back

image-20230428164252392

recoverSimilar to java catch.

func TestRecover(t *testing.T) {
    
    
	defer func() {
    
    
		if err := recover(); err != nil {
    
    
			fmt.Println("recovered from", err)
		}
	}()
	fmt.Println("Start")
	panic(errors.New("something wrong"))
}

image-20230428183030584

image-20230428165703129

In fact, the above repair method is very dangerous.

We must be careful what we revocerare doing, because our revocer does not detect what error occurred, but just records it or ignores it directly. At this time, some core resources of the system may be exhausted, but we force it to recover After that, the system will still not work normally, and our health check program will health checknot be able to detect the current system problems, because many health checks only check whether the current system process is in or not, because our process is in, so it will Formed 僵尸进程, it is alive, but it cannot provide services.

image-20230428165726627

If this kind of problem occurs, we can use “Let is Crash”the recoverable design pattern, we crash it directly, so that the daemon process will restart the service process (it’s a bit grand, but it’s actually restarting), restarting is to restore uncertainty The best way to go wrong.

package package

Build reusable modules (packages)

image-20230428183701611

image-20230428185412938

my_series.go

package series

func GetFibonacci(n int) ([]int, error) {
    
    
	ret := []int{
    
    1, 1}
	for i := 2; i < n; i++ {
    
    
		ret = append(ret, ret[i-1]+ret[i-2])
	}
	return ret, nil
}

package_test.go

Referencing a method in another package:

package client

import (
	"mygoland/geekvideo/ch13/series" // 包路径要从自己的gopath开始写起
	"testing"
)

func TestPackage(t *testing.T) {
    
    
	t.Log(series.GetFibonacci(10))
}

init method

image-20230428185555174

func init() {
    
    
	fmt.Println("init1")
}

func init() {
    
    
	fmt.Println("init2")
}

func GetFibonacci(n int) []int {
    
    
	ret := []int{
    
    1, 1}
	for i := 2; i < n; i++ {
    
    
		ret = append(ret, ret[i-1]+ret[i-2])
	}
	return ret
}

image-20230428185810992

How to use remote packages

image-20230428192434197

ConcurrentMap for GO

https://github.com/easierway/concurrent_map

go getImport using the command

go get -u github.com/easierway/concurrent_map

image-20230428192052651

package remote

import (
	cm "github.com/easierway/concurrent_map" // 导入远程包
	"testing"
)

func TestConcurrentMap(t *testing.T) {
    
    
	m := cm.CreateConcurrentMap(99)
	m.Set(cm.StrKey("key"), 10)
	t.Log(m.Get(cm.StrKey("key")))
}

image-20230428192409198

dependency management

Go unresolved dependency issues

image-20230429184940847

vendor path

image-20230429185019589

Commonly used dependency management tools

install glide

  • Mac environment, use brew to install glide

    brew install glide
    

    Successful installation
    image-20230430132905106

  • Initialize glide

    glide init
    

    image-20230505090852387

    image-20230505092905006

    image-20230505093034649

    image-20230505093114018

    image-20230505093146424

    image-20230505093229266

    image-20230505093327934

  • After glide init is executed, a file is generated yaml, and the dependent packages and version numbers are defined in it

    image-20230505093453057

  • Execute in the previous directoryglide install

    image-20230505094627270

    venderThen a directory and glide.lockfile will be generated under our specified file .

    image-20230505094650525

  • So far, Go can search for packages under the vendor directory, and we specify the path and version number of the package through vendor, which means that different versions of the same package can be used in the same environment.

The notes are organized from the Geek Time video tutorial: Go language from entry to actual combat

Guess you like

Origin blog.csdn.net/weixin_53407527/article/details/130854848