golang学习笔记(6)--面向接口编程(1)

一、    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语言的接口:
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(){}
这样就是非入侵式接口设计,不在接口使用继承。好像最后总结下爱就这么一句话,不在接口关系中使用继承与及接口实现。
 刚刚留了一个问题,入侵式与非入侵式的比较,从上面也可以看到入侵式接口设计类别层级什么的十分清晰,没有那么多依赖,解释性也比较好。非入侵式呢?相对比较灵活,简单,接口复用率高。(这都是我的理解哈,不代表官方说法)

猜你喜欢

转载自www.cnblogs.com/luohuayu/p/9197894.html