zoukankan      html  css  js  c++  java
  • golang学习笔记(6)--面向接口编程

    一、    duck typing

    duck typing意思是鸭子类型,我们把具备鸭子的行为等部分特征的一个东西叫做鸭子,这是鸭子类型的解释。其实,在go语言中是采用鸭子类型这种思想来实现接口这种编程方式的,我们把一个类只要实现了某接口的方法,我们就说他是这个接口的实现类。如下:

    我们定义了一个接口:

    type duck interface {
       Get(s string) string //不用func修饰这个方法
    }

    下面写一个类:
    type TheClass struct {
       S string
    }
    这个类实现了上面接口中的方法,如下,并不需要声明我们要实现某个接口。
    func (tc TheClass) Get(s string) string  {
       return tc.S;
    }

    实现类前面的类型是属于值类型传递,因为我们的接口里面实际是包了一个类型信息与及指针,所以实现一般采用直接的值传递而不是指针传递,因为一样可以对内容修改。

    如上通过go语言的方式来说明了什么是鸭子类型。

    二、    go语言中的接口。

    上面讲过了接口的实现方式。

    type duck interface {
       Get(s string) string
    }

    之前我们有讲过两种go语言的两种“继承“方式,一种是类似于装饰的叫做组合,在类里面放”父类“,另一种是取别名,当然取别名算不上,他不可以增加成员变量。那么接口可以”继承“吗?嗯,当然可以,如下

    type littleDuck interface {
       duck
       A() string
    }
    这样应该叫接口的“继承”
    刚刚在golang社区看到一篇被喷的文章,下面有个人这样说:“golang的接口是非入侵式的,楼主让非得把(**)接口写入到(**)结构中,真是人才啊“
    不是很理解什么意思,难道不能够用接口继承吗?问题先留在这里,以后再解决。
    (n分钟过后)
    查了下什么叫入侵式与非入侵式,附上自己的理解:入侵式是指要申明接口实现了某一个接口什么的,但是非入侵式就不用,一般的oo语言例如java就是入侵式接口设计,需要说明实现,有层级,但是go语言提倡是非入侵式,相对入侵式可能更灵活一点,不那么多依赖,但是这两种设计都各有优点,什么优点?看完下面的就知道了,接下来用例子来说下自己的理解吧。
    à: 
    Bird 接口 : fly(),run();
    Chicken 接口 : run()
    Wild 接口 :fly()
    如上3个接口,在入侵式接口设计中,chicken接口与wild接口需要去继承bird接口,因为他们属于这个大类,那么我们创建一个chicken的实现类对象的话,只需要实现chicken,然后wild同理。这样做就是入侵式接口设计的思路。
    在java中像这样:
    interface Bird{
        void run();
        void fly();
    }
    interface Wild extends Bird{
        void run();
    }
    interface Chicken extends Bird{
        void fly();
    }
    如果我们想要创建一个Wild的类型的类那么就需要创建一个,需要给wild写个实现类,一个Chicken类型的类就需要写一个Chicken实现类,Bird就需要写一个Bird的实现类,显然接口的复用性不高。当然这样的类型是相当清晰的。
    同样有另外一种写法,这样算是入侵式设计的思想,如下:
    interface Run{
        void run();
    }
    interface Fly{
        void fly();
    }

    interface Bird1 extends Run,Fly{}
    java的接口继承是支持多继承的
    这样的写法是一种组装的思想,这也是oo语言中的入侵式接口设计思路,通过继承的方式组装好,然后去写Bird1的实现类,没什么问题。
    再看看非入侵式:go语言推荐使用非入侵式接口设计,在写实现类的时候不需要说明实现哪个接口,也不需要去思考这是哪个接口的实现类,只要实现相应的方法就行,一般推荐这样写:
    type chicken interface {
       run()
    }
    type wild interface {
       fly()
    }
    type BirdImpl struct {
    }
    func (b BirdImpl) run()  {
    }
    func (b BirdImpl) fly(){
    }
    如上的方式很灵活,我们直接可以birdImpl创建一个类,然后实现一个方法他就属于某一个类型,不用去组装接口。当然,我们也可以通过实现接口的方式来实现接口,如:
    type Bird interface {
       run()
       fly()
    }
    type chicken interface {
       run()
    }
    type wild interface {
       fly()
    }
    这样的方式相当于bird实现了两个接口的接口,我要创建实体对象的时候需要再创建一个类:
    type BirdImpl struct {}
    func (b BirdImpl) run(){}
    func (b BirdImpl) fly(){}
    这个类从这里看必定就实现了上述的3个接口,这样的写法其实是可以的,但是看起来冗余了许多,我完全可以不需要Bird 或者另外两个接口。Go语言这样的接口设计方式相对更简单、灵活了。当然为了更具解释性,我们可以把wild名改成Fly,chicken改成Run。当然这样的组合方式在go语言使用还是不少,因为通过组合的方式也很方便灵活
    那么我们可以这样来设计go语言的接口:
    type Bird interface {
       run()
       fly()
    }
    type BirdImpl struct {}
    func (b BirdImpl) run()  {}
    func (b BirdImpl) fly(){}
    或者这样:
    type Run interface {
       run()
    }
    type Fly interface {
       fly()
    }
    type BirdImpl struct {}
    func (b BirdImpl) run()  {}
    func (b BirdImpl) fly(){}
    这样就是非入侵式接口设计,不在接口使用继承。好像最后总结下爱就这么一句话,不在接口关系中使用继承与及接口实现。
     刚刚留了一个问题,入侵式与非入侵式的比较,从上面也可以看到入侵式接口设计类别层级什么的十分清晰,没有那么多依赖,解释性也比较好。非入侵式呢?相对比较灵活,简单,接口复用率高。(这都是我的理解哈,不代表官方说法)
    网上对入侵式与非入侵式的观点很多,以上只是我的理解,而且go语言的源码里面也有很多组合接口的地方,我也很难说,这两个概念就先放一放。
    三、               接口的使用。
    首先说下接口变量的结构,其内部有两个东西,一个是接口实现者的类型,一个是接口实现者的指针,我们来看一段段代码:
    var o A
     o = class.B{}
     fmt.Printf("%T,%p",o,o)
    这里的B实现了接口A,T是类型,v是值,可以知道o里面实际含有两个人东西。同时要提下,如果在不同包下进行接口实现,记住大写方法名首字母,这样才是public的。
    接下来我们讲下接口的组合,上面讲到接口的组合不被推荐,说法是有问题的,通过后面的学习,接口是可以被组合的,而且在go的源码中还大量被用到,这种组合的方式是很方便的,当然存在的问题也是问题,这个就不去争议了,网上观点不一。
    接口的组合上文已经讲过了,这里我们来讲讲interface{},interface{}代表go语言里面的所有类型,意义和java的Object一样,但是go不是说所有类都继承interface,没有这种说法:
    i := [...]interface{}{1,"2",3,"a","v"}
     fmt.Println(i)
    如上代码,数组元素可以是任意类型,当然可以作为函数传入值类型的时候,就表示可以传入任意类型。
    四、               Go两种常用接口。
    a)  stringer
    这个接口相当与java的toString()方法,我们只要实现string()string方法,就算是实现了这个接口,直接打印对象的时候自动调用string。
    func (b B) String() string {
      return "实现了stringer的B"
    }
    比如这样写。
    b)  reader/writer
    file其实是实现了reader与writer两个接口的,一般我们需要reader或者writer实例的时候可以传一个file进去也是一样的 ,这里可以用组合
    type file interface{
    Reader
    Writer
    }
     
  • 相关阅读:
    .net 自带的两个内置委托
    Socket
    SQL EXISTS与IN的区别(2)
    一个选择题,写了一个类
    SQL Server- 存储过程(2)
    VS插件-JSEnhancements
    SQL Server- 存储过程(1)
    MVC-工作原理
    C#-属性
    SQL Server 中游标的使用
  • 原文地址:https://www.cnblogs.com/luohuayu/p/9197894.html
Copyright © 2011-2022 走看看