객체 지향 프로그래밍
Go 언어의 객체 지향 프로그래밍은 다른 언어와 매우 다릅니다.
Go는 객체 지향 언어입니까?
예, 아니오 . Go에는 유형과 메소드가 있고 객체 지향 프로그래밍 스타일을 허용하지만 유형 계층 구조(상속)는 없습니다. Go의 "인터페이스" 개념은 사용하기 쉽고 어떤 면에서는 더 일반적이라고 생각되는 다른 접근 방식을 제공합니다. 서브클래싱과 유사하지만 동일하지는 않은 것을 제공하기 위해 다른 유형 내에 유형을 임베드하는 방법도 있습니다. 또한 Go의 메서드는 C++ 또는 Java의 메서드보다 더 일반적입니다. 일반적인 "unboxed" 정수와 같은 기본 제공 유형을 포함하여 모든 유형의 데이터에 대해 정의할 수 있습니다. 구조(클래스)에 국한되지 않습니다.
또한 유형 계층 구조가 없기 때문에 Go의 "객체"는 C++ 또는 Java와 같은 언어의 "객체"보다 훨씬 가볍게 느껴집니다.
데이터 및 동작 캡슐화
구조 정의
인스턴스 생성 및 초기화
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)
}
동작(방법) 정의
-
첫번째
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()) }
따라서 이러한 작성 방식은 복사의 오버헤드를 갖게 됩니다.
-
두번째
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()) }
이것이 더 권장됩니다.
인터페이스(상호작용 프로토콜 정의)
Go의 인터페이스는 많은 주류 프로그래밍 언어의 인터페이스와 매우 다릅니다.
자바 코드의 예:
오리 유형 인터페이스
Go 언어의 인터페이스:
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())
}
인터페이스 이동
인터페이스 변수
맞춤 유형
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")
}
위 테스트 코드의 출력은 다음과 같습니다.
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")
}
최종 출력은 다음과 같습니다.
하위 클래스가 상위 클래스의 공개 멤버를 사용할 수 있다는 사실에 상속이 기본 설정되기 때문에 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의 코드를 볼 수 있습니다.
다형성
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)
}
빈 인터페이스 및 어설션
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)
}
Go 인터페이스 모범 사례
오류 메커니즘
오류
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)
}
}
모범 사례
조기에 실패하고 중첩을 피하십시오!
예:
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)
}
공황
공황 대 os.Exit
다시 전화
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"))
}
사실 위의 수리 방법은 매우 위험합니다.
revocer
우리의 revocer는 어떤 오류가 발생했는지 감지하지 못하고 그냥 기록하거나 무시하기 때문에 주의가 필요합니다 . 시스템은 여전히 정상적으로 작동하지 않을 것이며, 상태 검사 프로그램은 health check
현재 시스템 문제를 감지할 수 없습니다. 많은 상태 검사는 현재 시스템 프로세스가 활성화되어 있는지 여부만 확인하기 때문입니다. 우리 프로세스가 존재하기 때문에 형성됩니다. 僵尸进程
, 살아 있지만 서비스를 제공할 수 없습니다.
이런 종류의 문제가 발생하면 복구 가능한 디자인 패턴을 사용할 수 있으며 “Let is Crash”
직접 크래시하여 데몬 프로세스가 서비스 프로세스를 다시 시작합니다(조금 웅장하지만 실제로는 다시 시작합니다). 다시 시작하는 것은 불확실성을 복원하는 것입니다. 잘못되는 가장 좋은 방법.
패키지 패키지
재사용 가능한 모듈(패키지) 구축
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))
}
초기화 방법
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
}
원격 패키지 사용 방법
GO용 ConcurrentMap
https://github.com/easierway/concurrent_map
명령을 사용하여 go get
가져오기
go get -u github.com/easierway/concurrent_map
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")))
}
종속성 관리
해결되지 않은 종속성 문제 이동
벤더 경로
일반적으로 사용되는 종속성 관리 도구
- 고뎁: https://github.com/tools/godep
- 글라이드: https://github.com/Masterminds/glide
- 출발: https://github.com/golang/dep
글라이드 설치
-
Mac 환경, 글라이드 설치는 brew 사용
brew install glide
성공적인 설치
-
글라이드 초기화
glide init
-
glide init 실행 후 파일이 생성되고
yaml
그 안에 종속 패키지 및 버전 번호가 정의됩니다. -
이전 디렉토리에서 실행
glide install
vender
그러면 지정된 파일 아래에 디렉토리와glide.lock
파일이 생성됩니다 . -
지금까지 Go는 벤더 디렉토리에서 패키지를 검색할 수 있으며 벤더를 통해 패키지의 경로와 버전 번호를 지정합니다. 즉, 동일한 패키지의 다른 버전을 동일한 환경에서 사용할 수 있습니다.
메모는 Geek Time 비디오 자습서에서 구성됩니다. 시작부터 실제 전투까지 언어 사용