V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
xiangdong1987
V2EX  ›  Go 编程语言

Go 学习笔记 5(2)

  •  1
     
  •   xiangdong1987 · 2019-05-24 13:49:36 +08:00 · 2439 次点击
    这是一个创建于 2042 天前的主题,其中的信息可能已经有所发展或是发生改变。

    通过嵌入结构体来扩展类型

    • 嵌入式写法,简化写代码时类型适用声明
    
    import "image/color"
    
    type Point struct{ X, Y float64 }
    
    type ColoredPoint struct {
        Point
        Color color.RGBA
    }
    
    var cp ColoredPoint
    cp.X = 1
    fmt.Println(cp.Point.X) // "1"
    cp.Point.Y = 2
    fmt.Println(cp.Y) // "2"
    
    • 对于 Point 中的方法我们也有类似的用法,我们可以把 ColoredPoint 类型当作接收器来调用 Point 里的方法,即使 ColoredPoint 里没有声明这些方法:
    red := color.RGBA{255, 0, 0, 255}
    blue := color.RGBA{0, 0, 255, 255}
    var p = ColoredPoint{Point{1, 1}, red}
    var q = ColoredPoint{Point{5, 4}, blue}
    fmt.Println(p.Distance(q.Point)) // "5"
    p.ScaleBy(2)
    q.ScaleBy(2)
    fmt.Println(p.Distance(q.Point)) // "10"
    
    • Point 类的方法也被引入了 ColoredPoint。用这种方式,内嵌可以使我们定义字段特别多的复杂类型,我们可以将字段先按小类型分组,然后定义小类型的方法,之后再把它们组合起来。

    嵌入并不是代表继承的意思,更像是组合。并不是嵌入了某种类型这个类型类型就是某种类型的子类型,应该说是组合起来适用的更为合适。 如果想继承方法可以用匿名字段或者引用字段来做继承

    type ColoredPoint struct {
        *Point
        Color color.RGBA
    }
    
    p := ColoredPoint{&Point{1, 1}, red}
    q := ColoredPoint{&Point{5, 4}, blue}
    fmt.Println(p.Distance(*q.Point)) // "5"
    q.Point = p.Point                 // p and q now share the same Point
    p.ScaleBy(2)
    fmt.Println(*p.Point, *q.Point) // "{2 2} {2 2}"
    
    • 就是因为有了内嵌才有下面的小技巧:
    var (
        mu sync.Mutex // guards mapping
        mapping = make(map[string]string)
    )
    
    func Lookup(key string) string {
        mu.Lock()
        v := mapping[key]
        mu.Unlock()
        return v
    }
    
    • 可以改写成如下:
    var cache = struct {
        sync.Mutex
        mapping map[string]string
    }{
        mapping: make(map[string]string),
    }
    
    
    func Lookup(key string) string {
        cache.Lock()
        v := cache.mapping[key]
        cache.Unlock()
        return v
    }
    
    • 使代码更简洁更优雅

    方法值和方法表达式

    • 类型中的函数调用分两步
      • 用选择器选择方法,指定接收器
      • 根据接收器和参数返回方法值
    • 我们可以根据不同的类型来决定用类型下的什么方法,通过指定不同接收器的方法,如下例子:
    type Point struct{ X, Y float64 }
    
    func (p Point) Add(q Point) Point { return Point{p.X + q.X, p.Y + q.Y} }
    func (p Point) Sub(q Point) Point { return Point{p.X - q.X, p.Y - q.Y} }
    
    type Path []Point
    
    func (path Path) TranslateBy(offset Point, add bool) {
        var op func(p, q Point) Point
        if add {
            op = Point.Add
        } else {
            op = Point.Sub
        }
        for i := range path {
            // Call either path[i].Add(offset) or path[i].Sub(offset).
            path[i] = op(path[i], offset)
        }
    }
    

    封装

    • 一个对象的变量或者方法如果对调用方是不可见的话,一般就被定义为“封装”。封装有时候也被叫做信息隐藏,同时也是面向对象编程最关键的一个方面。
    • 封装的优点
      • 首先,因为调用方不能直接修改对象的变量值,其只需要关注少量的语句并且只要弄懂少量变量的可能的值即可。
      • 第二,隐藏实现的细节,可以防止调用方依赖那些可能变化的具体实现,这样使设计包的程序员在不破坏对外的 api 情况下能得到更大的自由。
      • 第三个优点也是最重要的优点,是阻止了外部调用方对对象内部的值任意地进行修改。

    总结

    我们学到了如何将方法与命名类型进行组合,并且知道了如何调用这些方法。尽管方法对于 OOP 编程来说至关重要,但他们只是 OOP 编程里的半边天。为了完成 OOP,我们还需要接口。Go 里的接口会在接下来的学习中学习到。

    转自 https://xiangdong1987.github.io/

    目前尚无回复
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2375 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 22ms · UTC 16:08 · PVG 00:08 · LAX 08:08 · JFK 11:08
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.