Go Duck Typeing & Interface & Closure

Duck Typeing & Interface

Duck Typing

Duck typing in computer programming is an application of the duck test -- "if it walks like a duck and it quacks like a duck, then it must be a duck"

Duck Typing in py

def duck_test(duck):
    try:
        duck.quack()
        duck.walk()
    except (AttributeError, TypeError):
        print("can't quack()")

Duck Typing in java

image-20210424175638309

Duck Typing in Go

非侵入式 Interface

非侵入式:不需要显示声明

当结构体的方法是某个接口的超集的时候 就可以认为该结构体实现了某接口

好处:降低耦合度 增加了可扩展性

package main

type person struct {
    name string
}

func (p person)sing() {
    fmt.Printf(p.name,"sing")
}
func (p person)dance() {
    fmt.Printf(p.name,"dance")
}
func (p person)rap() {
    fmt.Printf(p.name,"rap")
}
func (p person)playBasketball() {
    fmt.Printf(p.name,"playBasketball")
}

func (p person)playSoccer() {
    fmt.Printf(p.name,"playSoccer")
}
func (p person)fly() {
    fmt.Printf(p.name,"fly")
}

func show(t Trainee) {
    t.sing()
    t.dance()
    t.rap()
    t.playBasketball()
}

type Trainee interface {
    sing()
    dance()
    rap()
    playBasketball()
}

func play(p Player){
    p.playBasketball()
    p.playSoccer()
}
type Player interface{
    playBasketball()
    playSoccer()
}

func showOff(superman Superman){
    superman.fly()
}

type Flyer interface {
    fly()
}
// 组合接口
type Superman interface {
    Flyer
    Trainee
}

func main() {
    joey := person("joey")
    show(joey)
    play(joey)
    showOff(joey)
}

Memory Allocation and Escape Analysis

Go内存分配与逃逸分析

堆栈内存

  • 堆Heap
    • 可以动态分配内存大小,适合不可预知大小的内存分配
    • 声明周期享有自主控制权
    • 复杂的内存管理会占用资源 速度慢 存在内存碎片已经内存泄露问题
  • 栈Stack
    • 存取数据要比堆快很多
    • 缺乏灵活性 栈中数据大小与编译器挂钩 生命周期是确定的
    • 与函数共存亡

逃逸分析Escape Analysis

根据堆栈数据存储不同的特点,需要对程序中的数据选择合适的区域进行存放。

GC语言将堆栈内存的分配对于程序员变的透明。逃逸分析就是编译器能分析代码特征决定数据应该用堆还是栈来进行内存分配。

JVM也可以通过参数开启逃逸分析

逃逸失败案例:

栈中变量a在函数结束的时候会被弹栈 地址空间不存在

#include <stdio.h>
int *returnArr(){
    int a = 3;
    return &a;
}
int main() {
    return 0;
}

go中编译器做的逃逸分析

package main
func returnArr() *int{
    a:=3
    return &a
}
func main() {
    
}

go build -gcflags '-m -l' main.go查看内存逃逸情况

go tool compile -S main.go 查看局部变量是否真的进入heap中runtime.newObject(SB)

package main
func returnArr() *int{
    a := new(int)
    return a // new 默认返回指针
}
func main() {
    
}

不引发逃逸行为

package main
func returnArr() int{
    a := new(int) // 取决于上下文的环境 来觉得是否又必要去堆还是栈中申请内存
    // 取决于变量是否会被函数外部引用
    return *a // 不会引发逃逸行为
}
func main() {
    
}

go tool compile -S main.go查看发现没有去堆中申请内存的操作

Go Closure闭包

Closure: 关闭,倒闭,道路封闭

闭包:

  • 函数可以作为值传递f := funcA , f := funcA()//接受返回值
  • 词法作用域,编写代码时确定的作用域,是静态的作用域。外部函数无法直接访问函数的局部变量

函数+函数内部的引用环境

示例:

package main
import "fmt"
func enemy() func() {
    key := 0
    return func() {
        key++
        fmt.Println(key)
    }
}

func main() {
    f := enemy() // 返回匿名函数func 以及函数内部的引用环境 key
    f() // 1
    f() // 2
    f() // 3
    f() // 4
}

Function Currying(函数柯里化)

  • 分阶段接受参数
  • 参数的复用 分阶段执行
package main

func adder(a, b int) {
    fmt.Println(a + b)
}

func curryAdder(a int) func(int) {
    return func(b int) {
        fmt.Println(a + b)
    }
}
func main() {
    adder(1, 2) // 一次执行到位 a + b
    addB := curryAdder(1) // =>  1 + b, b还没有传入参数
    fmt.Println("wait")
    addB(2) // => 1 + 2
    addB(3) // => 1 + 3, 1被复用
}

闭包存在内存泄露的风险 谨慎使用

原文地址:https://www.cnblogs.com/DengSchoo/p/14698255.html