golang技术随笔(一)深入理解interface
golang中的interface是什么
interface IFoo { void Bar(); } class Foo implements IFoo { void Bar(){} }
type IWriter interface { Write(buf [] byte) (n int, err error) } type File struct { // ... } func (f *File) Write(buf [] byte) (n int, err error) { // ... }
非侵入式接口一个很重要的好处就是去掉了繁杂的继承体系,我们看许大神在《go语言编程》一书中作的总结:
golang中的interface在面向对象思想中所扮演的角色
struct IPizzaCooker { virtual void Prepare(Pizza*) = 0; virtual void Bake(Pizza*) = 0; virtual void Cut(Pizza*) = 0; } struct PizzaDefaultCooker : public IPizzaCooker { Pizza* CookOnePizza() { Pizza* p = new Pizza(); Prepare(p); Bake(p); Cut(p); return p; } virtual void Prepare(Pizza*) { //....default prepare pizza } virtual void Bake(Pizza*) { //....default bake pizza } virtual void Cut(Pizza*) { //....default cut pizza } } struct MyPizzaCooker : public PizzaDefaultCooker { virtual void Bake(Pizza*) { //....bake pizza use my style } } int main() { MyPizzaCooker cooker; Pizza* p = cooker.CookOnePizza(); //.... return 0; }
本例子很简单,就是通过一个做pizza的类烹饪一个新pizza,烹饪的流程在父类中实现CookOnePizza,子类重写了Bake方法。下面我们看看golang中是如何实现这个例子的:
type IPizzaCooker interface { Prepare(*Pizza) Bake(*Pizza) Cut(*Pizza) } func cookOnePizza(ipc IPizzaCooker) *Pizza { p := new(Pizza) ipc.Prepare(p) ipc.Bake(p) ipc.Cut(p) return p } type PizzaDefaultCooker struct { } func (this *PizzaDefaultCooker) CookOnePizza() *Pizza { return cookOnePizza(this) } func (this *PizzaDefaultCooker) Prepare(*Pizza) { //....default prepare pizza } func (this *PizzaDefaultCooker) Bake(*Pizza) { //....default bake pizza } func (this *PizzaDefaultCooker) Cut(*Pizza) { //....default cut pizza } type MyPizzaCooker struct { PizzaDefaultCooker } func (this *MyPizzaCooker) CookOnePizza() *Pizza { return cookOnePizza(this) } func (this *MyPizzaCooker) Bake(*Pizza) { //....bake pizza use my style } func main() { var cooker MyPizzaCooker p := cooker.CookOnePizza() //.... }
由于golang的多态必须借助接口来实现,这实际上已不是严格意义上的依赖倒置了,在这个例子中golang显得有些笨拙,它其实完全可以有更优雅的实现方案,举这个例子只是为了给大家介绍多态在golang中的实现方式,以及所谓模拟继承并不等价于面向对象中的继承关系。
interface的内存布局
type Stringer interface { String() string } type Binary uint64 func (i Binary) String() string { return strconv.FormatUint(i.Get(), 2) } func (i Binary) Get() uint64 { return uint64(i) } func main() { var b Binary = 32 s := Stringer(b) fmt.Print(s.String()) }
interface在内存上实际由两个成员组成,如下图,tab指向虚表,data则指向实际引用的数据。虚表描绘了实际的类型信息及该接口所需要的方法集
s
:=
Stringer(b)这样的语句时,golang会生成Stringer接口对应于Binary类型的虚表,并将其缓存。参考文献
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。