zoukankan      html  css  js  c++  java
  • Golang笔记(二)面向对象的设计

    Golang笔记(二)面向对象的设计

    Golang本质还是面向过程的语言,但它实现了一些OOP的特性,包括抽象、封装、继承和多态。

    抽象和封装

    Golang和C语言一样以struct为数据结构核心,不同的是Golang的struct可以定义自己的函数,这使得struct有了一些class的特点,所以Golang具有OOP里抽象和封装的概念。举个栗子来说,f是os.OpenFile()函数返回的File类型指针,File结构体实现了Close()函数,通过f.Close()调用File结构体里的Close()函数。下面是File和其Close()函数的定义:

    type File struct {
        ...
    }
    ...
    func (f *File) Close() error {
    	...
    }
    

    Embedding模拟继承

    Golang提供一种称为组合(Composition)的方法实现类似继承的特性。本质上Golang并没有继承(extend)这个概念,Composition是通过在struct里进行嵌套包含的方式提供了类似继承的方法。Composition有两种形式:非匿名组合(has-a)和匿名组合(Pseudo is-a)。
    has-a就是简单的struct包含,struct A里包含了struct B,通过A.B.Func()调用了B里的Func(),这种直接调用的方式并不让人觉得是继承。
    Pseudo is-a即Golang的Embedding特性,通过在struct A里匿名字段来假装A从B里继承。
    看如下栗子,来理解一下Golang的Embedding机制:

    type A struct {
    	...
    }
    func (a *A) Func1() {
    	fmt.Printf("A Func1 is called")
    }
    func (a *A) Func2() {
    	fmt.Printf("A Func2 is called")
    }
    type B struct {
    	A //Embedded struct A
    	...
    }
    func (b *B) Func1() {
    	fmt.Printf("B Func1 is called")
    }
    func main() {
    	B.Func2() //B继承了A的Func2(),此处输出:A Func2 is called
    	B.Func1() //B重写了Func1(),此处输出:B Func1 is called
    }
    

    以上栗子说明Golang支持方法重写,但要注意它并不支持方法的重载。如下面代码A.Func2里调用a.Func1并不会被重载成B.Func1:

    func (a *A) Func2() {
    	fmt.Printf("A Func2 is called")
    	a.Func1()  //此处a还是A,并不会被重载为B
    }
    func main() {
    	B.Func2() //输出:A Func2 is called 和 A Func1 is called
    }
    

    除了不支持重载,Golang也不支持多继承。如struct C如果继承了struct A和struct B,必须显示引用A和B里的函数以区分其中相同的函数名。同样Embedding机制也不提供多态特性,在上面的栗子中将B类型变量赋值给A类型的变量会报编译错误。Golang并不符合面向对象中的一个重要基本原则--里氏代换原则(Liskov Substitution Principle LSP)。
    所以,Golang只是模拟继承特性。

    Interface实现多态

    Golang通过interface提供了多态的功能。Golang的interface设计最牛逼之处在于,任何数据结构只要实现了interface所定义的函数,就自动实现了这个interface。相比c++或者java要在class里进行冗长的声明,Golang的这个设计大大简化了interface的定义方式。Golang通过"interface"关键字定义了一套接口。如下代码是package "io"里定义的Reader和Writer接口:

    type Reader interface {
            Read(p []byte) (n int, err error)
    }
    type Writer interface {
            Write(p []byte) (n int, err error)
    }
    

    如下是一些常用的package的数据结构里实现的Reader和Writer接口:

    1. os.File.Read()和os.File.Write()
    2. strings.Reader.Read()
    3. bufio.Reader.Read()和bufio.Writer.Write()
    4. bytes.Buffer.Read()和bytes.Buffer.Write()

    当然你也可以定义自己的数据结构实现Reader和Writer接口。这样的数据结构变量即可直接用于interface变量的赋值。通过下面这个栗子即可快速理解Interface的作用:

    func myRead(r Reader) {
    	r.Read()
    }
    func main() {
    	f, err := os.Open("./file")
    	myRead(f) //use os.File as Reader Interface
    
    	r := strings.NewReader("this is a string")
    	myRead(r) //use strings.Reader as Reader Interface
    }
    

    综上,Golang实现了OOP的一些特性,使其易于面向对象的编程思路。

  • 相关阅读:
    H5及微信中唤起app的解决方案
    html5统计数据上报API:SendBeacon
    基于webpack4的react开发环境配置
    electron-vue开发爬坑指南
    利用git 进行多人协作开发
    js 性能优化利器:prepack
    各种渲染方式对比解析
    Nuxt.js部署应用的方式
    微信小程序--data的赋值与取值
    甘超波:什么是个人定位
  • 原文地址:https://www.cnblogs.com/jimbo17/p/8485377.html
Copyright © 2011-2022 走看看