GOLANG介面適配,組合方式的靈活介面演化
在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
,實現同樣的目的。
相關文章
- 介面卡模式-讓不相容的介面得以適配模式
- Android Q適配(非SDK介面管控)Android
- 深入理解介面隔離原則:構建靈活的面向介面軟體
- go語言介面避免無意被適配Go
- Kvaser—靈活多變的CAN匯流排介面方案
- golang中的介面Golang
- Unity學習疑問記錄之介面適配Unity
- Golang物件導向程式設計之繼承&虛基類【組合&介面】Golang物件程式設計繼承
- 簡潔/易用/靈活/高效->RecyclerView介面卡封裝View封裝
- 如何選擇合適的SSD介面?SSD固態硬碟介面型別詳解硬碟型別
- SpringBoot Controller介面接收資料(二)(適合萌新)Spring BootController
- android 螢幕適配一:通過自定義View的方式實現適配AndroidView
- 模仿微信適配 iPad 的佈局方式iPad
- 創業者需要的品質:靈活!靈活!靈活創業
- 精通 Golang 介面卡模式Golang模式
- 「Golang成長之路」面向介面Golang
- [譯] part 19: golang 介面 2Golang
- Golang介面簡單瞭解Golang
- 自動生成介面各種逆向組合引數
- 介面模組的定義
- 恢復失靈的Type-c介面
- Flutter螢幕適配,簡單粗暴的全域性適配方式Flutter
- [譯] iOS App 上一種靈活的路由方式iOSAPP路由
- 視窗介面設計規範:介面關閉方式及介面疊加
- 自適應介面設計
- 使用Golang的interface介面設計原則Golang
- 分享一份適合練手的介面測試實戰專案
- WebApi Swagger 介面多版本控制 適用於APP介面管理WebAPISwaggerAPP
- Vue3,用組合編寫更好的程式碼:靈活的引數(2/5)Vue
- golang 介面按需獲取資源Golang
- Golang 單元測試 - 介面層Golang
- golang拾遺:指標和介面Golang指標
- 「Golang成長之路」面向介面篇Golang
- [譯] part18: golang 介面 1Golang
- Android適配: 拉伸適配的缺點Android
- Android dp方式的螢幕適配工具使用(Android Studio外掛方式)Android
- PHP 以 SOAP 方式呼叫介面PHP
- Android MOCK HTTP 介面新方式AndroidMockHTTP
- Golang 學習——如何判斷 Golang 介面是否實現?Golang