原文:https://gocn.io/article/326
在OO(Object OrIEnted)原则中,有一条叫做:优先使用组合,而不是继承。虽然GolANG并不是OO的语言(没有继承和多态),但是不妨碍GolANG使用这条原则,而GolANG的作者就强调过这一点,在GolANG中是使用组合而非继承来扩展。
装逼的说来,继承是一种名词化的语言体系,先进行业务抽象然后设计类体系和继承关系。而组合,强制使用接口,因为组合中使用的总是另外一个对象的接口,通过动词的组合,实现目标,比如不管是什么只要有Write([]byte)(int,error)
这个动作,就实现了这个接口,其他对象组合这个接口后,对外也看起来就是个io.Writer
的接口。
比如,GOALNG1.8支持了writev
,一般在面向对象会这么的搞:
class Socket {int Write(voID*,int);int Writev(const iovec*,int);};
对的吧?一个Socket可以写数据,也可以用writev写iovec向量,就是一次性写入多个内存块。
Note: 有时候内存块是不连续的,比如一个VIDeo帧,发送给不同的客户端时,header是需要修改的,但是Payload都一样,那么可以针对每个客户端只创建一个header,然后公用payload,但是这时候两个内存指针是不连续的,特别是需要同时写入多个视频帧时,writev就很神奇的避免了内存拷贝
writev(header+payload)
,具体参考下writev的资料哈。
这样有个问题,并非所有系统都支持Writev的,并非所有Socket都支持Writev的,如果是自己写个代码,当然是可以随便这么搞的,但是作为标准库,GolANG当然是不能这么做的。GolANG就加了一个接口(一个新动作)叫做net.buffersWriter
,如果实现了这个接口就用writev。先看用法:
conn,err := net.Dial("tcp","127.0.0.1:1935") buffers := Buffers{ []byte("once upon a time in "),[]byte("Gopherland ... "),} buffers.Writeto(conn)
在Buffers的Writeto
方法会判断是否是writev的接口,如果是则用writev写,否则就一个个的写:
func (v *Buffers) Writeto(w io.Writer) (n int64,err error) { if wv,ok := w.(buffersWriter); ok { return wv.writeBuffers(v) }
实际上conn是net.TcpConn
,里面有个fd *net.netFD
,它实现了net.buffersWriter
接口,所以最后调用的就是(fd *netFD) writeBuffers(v *Buffers)
。
func (c *conn) writeBuffers(v *Buffers) (int64,error) { n,err := c.fd.writeBuffers(v)func (fd *netFD) writeBuffers(v *Buffers) (n int64,err error) { iovecs = append(iovecs,syscall.Iovec{Base: &chunk[0]}) wrote,_,e0 := syscall.Syscall(syscall.SYS_WRITEV,uintptr(fd.sysfd),uintptr(unsafe.Pointer(&iovecs[0])),uintptr(len(iovecs)))
对于其他没有实现这个接口的对象,就每个向量循环的写。
在看一个例子http.Get(url string)
,客户端发起一个http请求:
http.Get("http://localhost:1985/API/v1/versions") // 实际上调用的是:func (c *ClIEnt) Get(url string) // 然后调用:(c *ClIEnt) Do(req *Request)
在GolANG1.7中引入了context的概念,用来支持cancel,怎么用的:
ctx,cancel := context.WithCancel(context.Background())select {case <- ctx.Done(): // Cancelled.case <- time.After(...): // Timeoutcase <- other events: // Other events.}
如何支持取消的http请求呢?给http.Get
加个ctx参数?例如http.Get(ctx,url)
这样?那改动得多大啊,而且还不能兼容之前的API,泪奔~看看GolANG的解决:
ctx,cancel := context.WithCancel(context.Background())go func(){ req,err := http.NewRequest("http://...") res,err := http.DefaultClIEnt.Do(req.WithContext(ctx)) defer res.Body.Close() // 读取res响应结果。}()select {case <- ctx.Done():case <- time.After(3 * time.Second): cancel() // Timeout to cancel all requests.}
使用组合,通过req.WithContext
再返回一个*http.Request
,实现同样的目的。
以上是内存溢出为你收集整理的GOLANG接口适配,组合方式的灵活接口演化全部内容,希望文章能够帮你解决GOLANG接口适配,组合方式的灵活接口演化所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)