[입문부터 실전까지 언어를 가다] 객체지향 프로그래밍

객체 지향 프로그래밍

Go 언어의 객체 지향 프로그래밍은 다른 언어와 매우 다릅니다.

이미지-20230428111344935

Go는 객체 지향 언어입니까?

예, 아니오 . Go에는 유형과 메소드가 있고 객체 지향 프로그래밍 스타일을 허용하지만 유형 계층 구조(상속)는 없습니다. Go의 "인터페이스" 개념은 사용하기 쉽고 어떤 면에서는 더 일반적이라고 생각되는 다른 접근 방식을 제공합니다. 서브클래싱과 유사하지만 동일하지는 않은 것을 제공하기 위해 다른 유형 내에 유형을 임베드하는 방법도 있습니다. 또한 Go의 메서드는 C++ 또는 Java의 메서드보다 더 일반적입니다. 일반적인 "unboxed" 정수와 같은 기본 제공 유형을 포함하여 모든 유형의 데이터에 대해 정의할 수 있습니다. 구조(클래스)에 국한되지 않습니다.

또한 유형 계층 구조가 없기 때문에 Go의 "객체"는 C++ 또는 Java와 같은 언어의 "객체"보다 훨씬 가볍게 느껴집니다.

데이터 및 동작 캡슐화

구조 정의

이미지-20230428121213526

인스턴스 생성 및 초기화

이미지-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)
}

이미지-20230428122127640

동작(방법) 정의

이미지-20230428122402465

  • 첫번째

    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())
    }
    

    이미지-20230428123715323

    따라서 이러한 작성 방식은 복사의 오버헤드를 갖게 됩니다.

  • 두번째

    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())
    }
    

    이미지-20230428123601046

    이것이 더 권장됩니다.

인터페이스(상호작용 프로토콜 정의)

Go의 인터페이스는 많은 주류 프로그래밍 언어의 인터페이스와 매우 다릅니다.

자바 코드의 예:

이미지-20230428124443597

오리 유형 인터페이스

Go 언어의 인터페이스:

이미지-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())
}

이미지-20230428143644240

인터페이스 이동

이미지-20230428130027398

인터페이스 변수

이미지-20230428130325699

맞춤 유형

이미지-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
	}
}

확장 및 재사용(상속과 유사)

Go 언어는 자연스럽게 상속을 지원할 수 없지만 객체 지향 기능을 구현하려고 합니다.

즉, 부모 클래스 객체는 서브클래스 객체로 초기화되고, 부모 클래스 객체가 호출한 함수는 서브클래스가 구현한 함수이므로 LSP(Subclass Exchange Principle)를 만족한다.

사례 1 : Go 언어는 다음 코드와 같이 상위 클래스의 기능 확장을 지원합니다.

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")
}

위 테스트 코드의 출력은 다음과 같습니다.

이미지-20230428141543094

Pet's Speak를 호출하는 dog's SpeakTo에서 Dog's Speak가 호출되어 출력이 정상입니다.
Pet 및 Dog 호출은 서로 영향을 미치지 않으며 전적으로 사용자에게 달려 있습니다.

그러나 이것은 우리가 원하는 것과는 다릅니다. Pet 클래스에는 고유한 메서드가 있고 Dog 클래스에는 고유한 메서드가 있으며 둘의 범위가 완전히 다릅니다.

여기서 Go 언어는 익명의 중첩 유형을 도입합니다. 즉, Dog 클래스는 Pet 클래스와 동일한 이름으로 자체 메서드를 구현할 필요가 없으며 Pet 멤버는 Dog 클래스 선언에서 변경할 수 있습니다.

익명 중첩 유형

사례 2 : Go 언어는 익명 함수 유형을 지원합니다.

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

이런 식으로 Dog는 같은 이름으로 자신의 함수 멤버를 선언할 필요가 없으며 기본 호출은 Pet 멤버 함수의 호출입니다.

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")
}

최종 출력은 다음과 같습니다.

이미지-20230428141503654

하위 클래스가 상위 클래스의 공개 멤버를 사용할 수 있다는 사실에 상속이 기본 설정되기 때문에 Pet의 모든 멤버 함수가 호출되며 상속처럼 느껴집니다.

익명의 내포형 아래에서 Go 언어가 정말 상속을 지원하는지 충분히 시험해보고자 합니다.앞선 코드와 같이 Pet in Dog의 동명 함수를 구현할 수 있고, 부모 클래스 객체를 통해 하위 클래스의 멤버 메서드를 호출할 수 있습니다. , C++ /Java와 마찬가지로 이와 같이 업캐스팅을 수행합니다(자체적으로는 불가능하며 Go 언어는 명시적 타입캐스팅을 지원하지 않습니다).

사례 3 : Go 언어는 상속, 다음 코드를 지원하지 않습니다.

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类型

요약하면 Go 언어는 상속을 지원하지 않지만 인터페이스의 확장 및 재사용을 지원할 수 있습니다. **(익명 중첩 유형) ** 이 임베딩 방법은 상속으로 사용할 수 없습니다. 하위 클래스 메서드 데이터(오버로딩)는 LSP 원칙을 지원하지 않습니다.

그 중 확장이란 서로 다른 클래스가 동일한 멤버 함수를 구현하는 것을 의미하며 Case 1과 유사한 확장 인터페이스 형태를 구현할 수 있습니다.

재사용은 익명의 중첩 유형을 통한 오버로딩과 유사한 기능을 달성하는 것입니다.사례 2의 코드를 볼 수 있습니다.

다형성

이미지-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)
}

이미지-20230428144147613

빈 인터페이스 및 어설션

이미지-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)
}

이미지-20230428145558309

Go 인터페이스 모범 사례

이미지-20230428145718339

오류 메커니즘

오류

이미지-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)
	}
}

이미지-20230428161919642

모범 사례

이미지-20230428162534768

조기에 실패하고 중첩을 피하십시오!

예:

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)
}

공황

이미지-20230428163230055

공황 대 os.Exit

이미지-20230428163307433

다시 전화

이미지-20230428164252392

recover자바와 비슷합니다 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"))
}

이미지-20230428183030584

이미지-20230428165703129

사실 위의 수리 방법은 매우 위험합니다.

revocer우리의 revocer는 어떤 오류가 발생했는지 감지하지 못하고 그냥 기록하거나 무시하기 때문에 주의가 필요합니다 . 시스템은 여전히 ​​정상적으로 작동하지 않을 것이며, 상태 검사 프로그램은 health check현재 시스템 문제를 감지할 수 없습니다. 많은 상태 검사는 현재 시스템 프로세스가 활성화되어 있는지 여부만 확인하기 때문입니다. 우리 프로세스가 존재하기 때문에 형성됩니다. 僵尸进程, 살아 있지만 서비스를 제공할 수 없습니다.

이미지-20230428165726627

이런 종류의 문제가 발생하면 복구 가능한 디자인 패턴을 사용할 수 있으며 “Let is Crash”직접 크래시하여 데몬 프로세스가 서비스 프로세스를 다시 시작합니다(조금 웅장하지만 실제로는 다시 시작합니다). 다시 시작하는 것은 불확실성을 복원하는 것입니다. 잘못되는 가장 좋은 방법.

패키지 패키지

재사용 가능한 모듈(패키지) 구축

이미지-20230428183701611

이미지-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

다른 패키지의 메소드 참조:

package client

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

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

초기화 방법

이미지-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
}

이미지-20230428185810992

원격 패키지 사용 방법

이미지-20230428192434197

GO용 ConcurrentMap

https://github.com/easierway/concurrent_map

명령을 사용하여 go get가져오기

go get -u github.com/easierway/concurrent_map

이미지-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")))
}

이미지-20230428192409198

종속성 관리

해결되지 않은 종속성 문제 이동

이미지-20230429184940847

벤더 경로

이미지-20230429185019589

일반적으로 사용되는 종속성 관리 도구

글라이드 설치

  • Mac 환경, 글라이드 설치는 brew 사용

    brew install glide
    

    성공적인 설치
    이미지-20230430132905106

  • 글라이드 초기화

    glide init
    

    이미지-20230505090852387

    이미지-20230505092905006

    이미지-20230505093034649

    이미지-20230505093114018

    이미지-20230505093146424

    이미지-20230505093229266

    이미지-20230505093327934

  • glide init 실행 후 파일이 생성되고 yaml그 안에 종속 패키지 및 버전 번호가 정의됩니다.

    이미지-20230505093453057

  • 이전 디렉토리에서 실행glide install

    이미지-20230505094627270

    vender그러면 지정된 파일 아래에 디렉토리와 glide.lock파일이 생성됩니다 .

    이미지-20230505094650525

  • 지금까지 Go는 벤더 디렉토리에서 패키지를 검색할 수 있으며 벤더를 통해 패키지의 경로와 버전 번호를 지정합니다. 즉, 동일한 패키지의 다른 버전을 동일한 환경에서 사용할 수 있습니다.

메모는 Geek Time 비디오 자습서에서 구성됩니다. 시작부터 실제 전투까지 언어 사용

Supongo que te gusta

Origin blog.csdn.net/weixin_53407527/article/details/130854848
Recomendado
Clasificación