• Go面向接口篇


    Go语言是面向接口的,面向对象只支持封装,其余继承多态靠接口来实现的。

    接口

    接口是什么?一种约定,一个抽象的类型。

    接口作用

    注意:接口指定了类型应该具有的方法,类型决定了如何实现这些方法即接口由使用者定义。

    接口定义了一组方法,如果某个对象实现了某个接口的所有方法,则此对象就实现了该接口。

    定义接口

    接口可被任意的对象实现,一个对象可以实现任意多个接口

    任意的类型都实现了空interface(定义:interface{})即包含0个method的interface (表示任何类型:interface{}

    /* 定义接口 */
    type interface_name interface {
       method_name1 [return_type]
       method_name2 [return_type]
       method_name3 [return_type]
       ...
       method_namen [return_type]
    }
    接口变量:包括:实现者的类型、实现者的值    接口变量里面到底是值还是指针,可以选择。
    /* 定义结构体 */
    type struct_name struct {
       /* variables */
    }
    //接口实现是隐式的
    /* 实现接口方法 */
    func (struct_name_variable struct_name) method_name1() [return_type] {
       /* 方法实现 */
    }
    ...
    func (struct_name_variable struct_name) method_namen() [return_type] {
       /* 方法实现*/
    }

    接口实现

    func main() {
        var a animal
        var c cat
        a=c
        a.printInfo()  //a cat
        //使用另外一个类型赋值
        var d dog
        a=d
        a.printInfo() //a dog
    }
    
    type animal interface {
        printInfo()
    }
    
    type cat int
    type dog int
    
    func (c cat) printInfo(){
        fmt.Println("a cat")
    }
    
    func (d dog) printInfo(){
        fmt.Println("a dog")
    }

    接口的值是一个两个字长度的数据结构,第一个字包含一个指向内部表结构的指针,这个内部表里存储的有实体类型的信息以及相关联的方法集;第二个字包含的是一个指向存储的实体类型值的指针。所以接口的值结构其实是两个指针,这也可以说明接口其实一个引用类型。

    方法集

    如果要实现一个接口,必须实现这个接口提供的所有方法。

    实现接口有两种接收者?指针接收者、值接收者

    实体类型以值接收者实现接口的时候,不管是实体类型的值,还是实体类型值的指针,都实现了该接口。

    实体类型以指针接收者实现接口的时候,只有指向这个类型的指针才被认为实现了该接口。

    总结1:首先以方法接收者是值还是指针的角度看

    Methods ReceiversValues
    (t T) T and *T
    (t *T) *T

    如果是值接收者,实体类型的值和指针都可以实现对应的接口;如果是指针接收者,那么只有类型的指针能够实现对应的接口。

    func main() {
        var c cat
        var d dog
        //值作为参数传递
        invoke(c)  //invoke(&c)也可以
        //指针作为参数传递
        invoke(&d) //必须是这个!!!
    }
    //需要一个animal接口作为参数
    func invoke(a animal){
        a.printInfo()
    }
    
    type animal interface {
        printInfo()
    }
    
    type cat int
    type dog int
    
    //值接收者实现animal接口
    func (c cat) printInfo(){
        fmt.Println("a cat")
    }
    //指针接收者实现animal接口
    func (d *dog) printInfo(){
        fmt.Println("a dog")
    }

    结果:

    a cat
    a dog

    例子:比如queue中目前只能支持int类型

    //只支持int类型  注意这里举例只列出了Push实际操作记得将Pop也修改

    type Queue []int

    func (q *Queue) Push(v int) {
    *q = append(*q, v)
    } 

    //支持任何类型

    type Queue []interface{}

    func (q *Queue) Push(v interface{}) {
    *q = append(*q, v)
    }

    例子:1)如果只想支持int类型  2)例子:假如接口肚子都没限定类型,如何限定int

    duck typing 

    并不是Go语言特有的

    go属于结构化类型系统,类似duck typing(需要动态绑定)。这里假设Go结构duck typing

    其他语言中的duck typing

    Python中的:
    def download(retriever):
          return retriever.get("www.baidu.com")
    注意:运行时候才知道传入的retriever有没有get
    通常需要注释来说明接口
    
    C++中的:本身就支持type typing  通过模板来支持
    template <class R>
    string download(const R& retriever) {
         return retriever.get("www.baidu.com")
    注意:编译时才知道传入的retriever有没有get
    需要注释来说明接口(因为敲代码时候不知道)
    
    Java中没有duck typing 类似代码是:
    <R extends Retriever>
    String download(R r){
         return r.get("www.baidu.com");
    }
    注意:传入的参数必须实现Retriever接口 与Python与C++相比不需要注释说明
    不是duck typing
    问题:多个需求必须让R实现多个接口,这就有问题了

     Go嵌入类型

    一种可以把已有的类型声明在新的类型里的一种方式,其他语言中,继承做。

    Go提倡的代码复用的方式是组合,用组合代替继承。

    嵌入后,被嵌入的类型称之为内部类型、新定义的类型称之为外部类型,这里user就是内部类型,而admin是外部类型。

    func main() {
        ad:=admin{user{"张三","zhangsan@flysnow.org"},"管理员"}
        fmt.Println("可以直接调用,名字为:",ad.name)
        fmt.Println("也可通过内部类型调用,名字为:",ad.user.name)
        fmt.Println("但是新增加的属性只能直接调用,级别为:",ad.level)
        ad.user.sayHello()  
        ad.sayHello()  //外部类型也可以声明同名的字段或者方法,来覆盖内部类型的
        }
    type user struct {
        name string
        email string
    
    }
    
    type admin struct {
        user
        level string
    }
    func (u user) sayHello(){
        fmt.Println("Hello,i am a user")
    }
    
    func (a admin) sayHello(){
        fmt.Println("Hello,i am a admin")
    }

    输出为:

    可以直接调用,名字为: 张三
    也可通过内部类型调用,名字为: 张三
    但是新增加的属性只能直接调用,级别为: 管理员
    Hello,i am a user
    Hello,i am a admin

    func main() {
        ad:=admin{user{"张三","zhangsan@flysnow.org"},"管理员"}
        sayHello(ad.user)//使用user作为参数
        sayHello(ad)//使用admin作为参数
    }
    type user struct {
        name string
        email string
    
    }
    //如果内部类型实现了某个接口,那么外部类型也被认为实现了这个接口
    type Hello interface{
        hello()
    }
    type admin struct {
        user
        level string
    }
    func (u user) hello(){
        fmt.Println("Hello,i am a user")
    }
    
    func sayHello(h Hello){
        h.hello()
    }

    常用的嵌入组合

    type Reader interface {
        Read(p []byte) (n int, err error)
    }
    
    type Writer interface {
        Write(p []byte) (n int, err error)
    }
    
    type Closer interface {
        Close() error
    }
    
    
    type ReadWriter interface {
        Reader
        Writer
    }
    
    type ReadCloser interface {
        Reader
        Closer
    }
    
    type WriteCloser interface {
        Writer
        Closer
    }
    View Code

    目录结构以及代码

    main.go

    package main
    
    import (
        "fmt"
    
        "time"
    
        "learngo/retriever/mock"
        "learngo/retriever/real"
    )
    //方法改名只影响到使用者
    type Retriever interface {
        Get(url string) string   //不需要加func
    }
    
    type Poster interface {
        Post(url string,
            form map[string]string) string
    }
    
    const url = "http://www.imooc.com"  //如果不加http://会报错
    //使用者
    func download(r Retriever) string {
        return r.Get(url)
    }
    
    func post(poster Poster) {
        poster.Post(url,
            map[string]string{
                "name":   "ccmouse",
                "course": "golang",
            })
    }
    //组合接口
    type RetrieverPoster interface {
        Retriever
        Poster
    }
    func session(s RetrieverPoster) string {
        s.Post(url, map[string]string{
            "contents": "another faked imooc.com",
        })
        return s.Get(url)
    }
    
    func main() {
        var r Retriever  //必须要去实现  mockretriever
    //r 肚子里究竟是什么?  一个类型,一个值(指针,或者拷贝的真实值)
        mockRetriever := mock.Retriever{
            Contents: "this is a fake imooc.com"}
        r = &mockRetriever   //值接收者可以去掉&,但是也可以加上
        inspect(r)
    //真实的Retriver   指针接收者必须要&地址
        r = &real.Retriever{
            UserAgent: "Mozilla/5.0",
            TimeOut:   time.Minute,
        }
    //方法1 inspect(r) //方法2
    // Type assertion 通过r.(*内容)来获得 可能会出错,为了防止出错加ok if mockRetriever, ok := r.(*mock.Retriever); ok { fmt.Println(mockRetriever.Contents) } else { fmt.Println("r is not a mock retriever") } fmt.Println( "Try a session with mockRetriever") fmt.Println(session(&mockRetriever)) //这里如果改为&retriever也可以 } //看看r肚子里面是什么东西 func inspect(r Retriever) { fmt.Println("Inspecting", r) fmt.Printf(" > Type:%T Value:%v ", r, r) fmt.Print(" > Type switch: ") switch v := r.(type) { case *mock.Retriever: fmt.Println("Contents:", v.Contents) case *real.Retriever: fmt.Println("UserAgent:", v.UserAgent) } fmt.Println() }

    mockretriever.go

    package mock
    
    import "fmt"
    //名字最好不要用mockRetriever这样会重复
    type Retriever struct {
        Contents string
    }
    //常用系统接口 相当于其他语言的toString
    func (r *Retriever) String() string {
        return fmt.Sprintf(
            "Retriever: {Contents=%s}", r.Contents)
    }
    //实现者还想实现Post方法  指针接收者
    func (r *Retriever) Post(url string,
        form map[string]string) string {
        r.Contents = form["contents"]
        return "ok"
    }
    //语言本身不需要说明 只需要实现Get方法即可
    func (r *Retriever) Get(url string) string {
        return r.Contents
    }

    retriever.go

    package real
    
    import (
        "net/http"
        "net/http/httputil"
        "time"
    )
    //真实的Retriever
    type Retriever struct {
        UserAgent string
        TimeOut   time.Duration   //一个时间段
    }
    
    func (r *Retriever) Get(url string) string {
        resp, err := http.Get(url)
        if err != nil {
            panic(err)
        }
    
        result, err := httputil.DumpResponse(
            resp, true)
    
        resp.Body.Close()  //读完了之后要去close它
    
        if err != nil {
            panic(err)
        }
    
        return string(result)
    }

    输出

    Inspecting Retriever: {Contents=this is a fake imooc.com}
     > Type:*mock.Retriever Value:Retriever: {Contents=this is a fake imooc.com}
     > Type switch: Contents: this is a fake imooc.com
    
    Inspecting &{Mozilla/5.0 1m0s}
     > Type:*real.Retriever Value:&{Mozilla/5.0 1m0s}
     > Type switch: UserAgent: Mozilla/5.0
    
    r is not a mock retriever
    Try a session with mockRetriever
    another faked imooc.com
    
    Process finished with exit code 0

     常用系统接口

    Stringer 相当于其他语言的toString
    type Stringer interface {
          String() string
    }

    Reader 接口 Writer接口 :对文件的抽象 其实不一定是文件可以是其他的

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

     接口的组合:

    type ReadWriter interface {
          Reader
          Writer
    }
  • 相关阅读:
    Java面向对象(继承、抽象类)
    Java面向对象(类、封装)
    Java基础语法(Eclipse)
    JavaScript new对象的四个过程
    原生js实现深复制
    es6 实现双链表
    快速排序
    跨域问题
    pm2 使用
    js冒泡排序
  • 原文地址:https://www.cnblogs.com/ycx95/p/9361952.html
Copyright © 2020-2023  润新知