GOLANG接口适配,组合方式的灵活接口演化
2017-05-15 20:54
246 查看
原文:https://gocn.io/article/326
在OO(Object Oriented)原则中,有一条叫做:优先使用组合,而不是继承。虽然GOLANG并不是OO的语言(没有继承和多态),但是不妨碍GOLANG使用这条原则,而GOLANG的作者就强调过这一点,在GOLANG中是使用组合而非继承来扩展。
装逼的说来,继承是一种名词化的语言体系,先进行业务抽象然后设计类体系和继承关系。而组合,强制使用接口,因为组合中使用的总是另外一个对象的接口,通过动词的组合,实现目标,比如不管是什么只要有
比如,GOALNG1.8支持了
对的吧?一个Socket可以写数据,也可以用writev写iovec向量,就是一次性写入多个内存块。
Note: 有时候内存块是不连续的,比如一个Video帧,发送给不同的客户端时,Header是需要修改的,但是Payload都一样,那么可以针对每个客户端只创建一个header,然后公用payload,但是这时候两个内存指针是不连续的,特别是需要同时写入多个视频帧时,writev就很神奇的避免了内存拷贝
这样有个问题,并非所有系统都支持Writev的,并非所有Socket都支持Writev的,如果是自己写个代码,当然是可以随便这么搞的,但是作为标准库,GOLANG当然是不能这么做的。GOLANG就加了一个接口(一个新动作)叫做
在Buffers的
实际上conn是
对于其他没有实现这个接口的对象,就每个向量循环的写。
在看一个例子
在GOLANG1.7中引入了context的概念,用来支持cancel,怎么用的:
如何支持取消的HTTP请求呢?给
使用组合,通过
在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(...): // Timeout case <- 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学习笔记1-组合和接口
- Ext.get() 和 Ext.query() 组合使用 实现 最灵活的取元素方式
- Golang OOP、继承、组合、接口
- 二十四、继承(一) 代码重用、继承方式、接口继承与实现继承、继承和重定义、继承和组合
- Ext.get() 和 Ext.query() 组合使用 实现 最灵活的取元素方式
- LKDBHelper的各种接口使用方式,及各种sql组合条件语句。
- Ext.get() 和 Ext.query()组合使用实现最灵活的取元素方式
- Ext.get() 和 Ext.query() 组合使用 实现 最灵活的取元素方式
- DS5000(DS5020/DS5100/DS5300)接口数量、类型及组合方式
- Golang 如何定义一个接口类型的切片,它可以用来存储混合类型的数据,又如何自定义错误信息输出,以及如何定义变参函数,还有字符串多种拼接方式
- Ext.get() 和 Ext.query() 组合使用 实现 最灵活的取元素方式
- 接口测试04_fiddler使用get/post方式查询自选股组合信息
- Ext.get() 和 Ext.query()组合使用实现最灵活的取元素方式
- 怎么对EDIUS中的组合滤镜进行灵活运用
- Thrift 的C与golang语言实现以及相互调用方式
- HttpClient方式调用接口的实例
- MyBatis第三种方式,xml和接口的结合,以及如何获得插入数据的返回参数、传入空的参数
- “金箍咒”我的全排列组合算法设计演化之二有趣的分解式嵌套
- 区别1.Runnable接口方式,数据是程共享的 2 接口可以多实现