封装数据和行为
数据封装
结构定义
type Employee struct { Id string Name string Age int }
field后面没有逗号
实例创建及初始化
e := Employee{"0", "Bob", 20} e1 := Employee{Name: "Mike", Age: 30} e2 := new(Employee) // 注意这里返回的引用/指针,相当于e := &Employee{} e2.Id = "2" // 与其他主要编程语言的差异,通过实例的指针访问成员不需要使用-> e2.Age = 22 e2.Name = "Rose"
检验一下类型
t.Logf("e is %T", e) t.Logf("e2 is %T", e2)
=== RUN TestCreateEmployeeObj
--- PASS: TestCreateEmployeeObj (0.00s)
func_test.go:79: e is fun_test.Employee
func_test.go:80: e2 is *fun_test.Employee
PASS
行为(方法)定义
与其他主要编程语言的差异
两种方式定义行为:
// 第一种定义方式在实例对应方法被调用时,实例成员会进行值复制 func (e Employee) String() string { return fmt.Sprintf("ID:%s-Name:%s-Age:%d", e.Id, e.Name, e.Age) } // 通常情况下为了避免内存拷贝我们使用第二种定义方式 func (e *Employee) String() string { return fmt.Sprintf("ID:%s/Name:%s/Age:%d", e.Id, e.Name, e.Age) }
接口
定义交互协议
Java
如果把AClient和接口A打包在一起为packageA,Almpl打包为B
那么A和B 是相互依赖的
一般是接口独立在一个package中,解决循环依赖
Go
Duck Type式接口实现
没有显示表明实现接口,只是实现了接口的方法WriteHelloWorld()
type Programmer interface { WriteHelloWorld() string } type GoProgrammer struct { } func (g *GoProgrammer) WriteHelloWorld() string { return "fmt.println(\"Hello World\")" } func TestClient(t *testing.T) { var p Programmer p = new(GoProgrammer) t.Log(p.WriteHelloWorld()) }
=== RUN TestClient
--- PASS: TestClient (0.00s)
func_test.go:117: fmt.println("Hello World")
PASS
与其他主要编程语言的差异
1.接口为非入侵性,实现不依赖于接口定义
2.所以接口的定义可以包含在接口使用者包内
接口变量
自定义类型
1.type IntConvertionFn func(n int) int
2.type MyPoint int
func timeSpent(inner func(op int)int) func(opt int) int { return func(n int) int { start := time.Now() ret := inner(n) fmt.Println("time spent:", time.Since(start).Seconds()) return ret } }
参数特别长,自定义一个类型
type IntConv func(op int) int func timeSpent(inner IntConv) func(opt int) int { return func(n int) int { start := time.Now() ret := inner(n) fmt.Println("time spent:", time.Since(start).Seconds()) return ret } }
扩展和复用
争议最大的一块