This is a creation in Article, where the information may have evolved or changed.
Understanding Go Interface
0. Introduction
In Golang, interface is a very important concept and feature, previously written two related articles: Golang "generic Programming", Talk about Golang interface and reflect. Then there was a topic:understanding Golang interface (Gopher China)-youtube on interface at the Gopher China 2017, the author is Francesc. So do a collation and supplement here.
1. What is Interface?
In object-oriented programming, a protocol or interface are a common means for unrelated objects) to Comm Unicate with all other. These is definitions of methods) and values which the objects agree upon in order to co-operate. -wikipedia
This is Wikipedia's definition of protocal, and the interface class, such as Protocal, is a very helpful way to understand. Protocol, Chinese is generally called a protocol, such as a TCP protocol in a network transmission. Protocol can be thought of as an agreement between the two sides in order to communicate, interface can be analogous.
In Golang, interface is an abstract type, as opposed to an abstract type, which is a concrete type (concrete type): Int,string. The following is an example of an IO package.
// Writer is the interface that wraps the basic Write method.//// Write writes len(p) bytes from p to the underlying data stream.// It returns the number of bytes written from p (0 <= n <= len(p))// and any error encountered that caused the write to stop early.// Write must return a non-nil error if it returns n < len(p).// Write must not modify the slice data, even temporarily.//// Implementations must not retain p.type Writer interface { Write(p []byte) (n int, err error)}// Closer is the interface that wraps the basic Close method.//// The behavior of Close after the first call is undefined.// Specific implementations may document their own behavior.type Closer interface { Close() error}
In Golang, interface is a set of method, which is a manifestation of duck-type programming. Do not care about attributes (data), only care about behavior (methods). In particular, you can customize your own struct and provide a specific interface method to use it as a interface. Here is a typical use of interface, defining functions when the parameters are defined as interface, call the function can be very flexible.
type MyInterface interface{ Print()}func TestFunc(x MyInterface) {}type MyStruct struct {}func (me *MyStruct) Print() {}func main() { var me Mystruct TestFunc(me)}
2. Why Interface
The following three reasons are given in Gopher China:
Writing generic algorithm (generic programming)
Hiding implementation detail (hide concrete implementation)
Providing interception points (don't know how to translate)
2.1 Writing generic algorithm
Strictly speaking, generic programming is not supported in Golang. Using generic programming in high-level languages such as C + + is very simple, so generic programming has always been the most golang. But using interface we can implement generic programming, I would simply say here, specifically, I can refer to the previous article. For example, we are going to write a generic algorithm, and the formal parameter definition takes interface, taking the standard library's sort as an example.
Package sort//A type, typically a collection, that satisfies sort. Interface can be//sorted by the routines in this package. the methods require that the//elements of the collection being enumerated by an integer index.type Interface Interface { //Len is the number of elements in the collection. len () int //Less reports whether the element wit H //Index I should sort before the element with index J. less (i, J int) bool //Swap swaps the elements With indexes I and J. swap (i, J int)}...//Sort sorts data.//It makes one call to data. Len to determine N, and O (N*log (n)) calls to//data. Less and data. Swap. The sort is isn't guaranteed to being stable.func sort (data Interface) { //Switch to heapsort if depth of 2*ceil (LG (N+1) ) is reached. n: = data. Len () maxdepth: = 0 for I: = N; i > 0; I >>= 1 { maxdepth++ } maxdepth *= 2 quicksort (data, 0, N, maxDepth)}
The formal parameter of the Sort function is a interface that contains three methods: Len()
, Less(i,j int)
Swap(i, j int)
. When used regardless of the element type of the array (int, float, string ...). ), as long as we implement these three methods we can use the Sort function, thus implementing "generic programming". One thing that's more troubling is that we need to customize the array. Here is an example.
type Person struct { Name stringAge int}func (p Person) String() string { return fmt.Sprintf("%s: %d", p.Name, p.Age)}// ByAge implements sort.Interface for []Person based on// the Age field.type ByAge []Person //自定义func (a ByAge) Len() int { return len(a) }func (a ByAge) Swap(i, j int) { a[i], a[j] = a[j], a[i]}func (a ByAge) Less(i, j int) bool { return a[i].Age < a[j].Age }func main() { people := []Person{ {"Bob", 31}, {"John", 42}, {"Michael", 17}, {"Jenny", 26}, } fmt.Println(people) sort.Sort(ByAge(people)) fmt.Println(people)}
In addition, FRANSESC in Gopher China also mentioned a more interesting thing to share with you. The following is a good guideline when we design the function.
Be conservative in what do you send, being liberal in what do you accept. -robustness Principle
Correspondence to Golang is:
Return Concrete Types, receive interfaces as parameter. -robustness Principle applied to Go
That said, but when we flip through the Golang source, some functions return value is also interface.
2.2 Hiding implement Detail
Hide the concrete implementation, this is very good understanding. For example, I design a function for you to return a interface, then you can only through the interface inside the method to do some operations, but the implementation of the internal is completely unknown. Francesc An example of the context. The context was first provided by Google and has now been incorporated into the standard library, and has been added on the basis of the original context: Cancelctx,timerctx,valuectx. Language expression sometimes slightly pale, look at the context package code it.
func WithCancel(parent Context) (ctx Context, cancel CancelFunc) { c := newCancelCtx(parent) propagateCancel(parent, &c) return &c, func() { c.cancel(true, Canceled) }}
Indicates that the upper Withcancel function returns a Context interface, but the specific implementation of this interface is the Cancelctx struct.
Newcancelctx returns an initialized Cancelctx.func Newcancelctx (parent Context) cancelctx { return cancelctx{ context:parent, done: make (Chan struct{}), }}//A Cancelctx can be canceled. When canceled, it also cancels all children//that implement Canceler.type cancelctx struct { context done Chan struct{}//Closed by the first cancel call. &NBSP;MU sync. Mutex children map[canceler]struct{}//set to nil by the first cancel call err &N Bsp;error //set to Non-nil by the first cancel Call}func (c *canc ELCTX) Done () <-chan struct{} { return C.done}func (c *cancelctx) ERR () error { c.mu.lock () & nbsp defer C.mu.unlock () return C.err}func (c *cancelctx) string () string { return FMt. Sprintf ("%v.withcancel", C.context)}
Although the specific struct (which implements the context interface) returned by the previous function in the upper and lower sides is not implemented, it is completely unaware of the user.
func WithCancel(parent Context) (ctx Context, cancel CancelFunc) //返回 cancelCtxfunc WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) //返回 timerCtxfunc WithValue(parent Context, key, val interface{}) Context //返回 valueCtx
2.3 Providing interception points
Francesc here interception want to express the meaning I understand should be wrapper or decorator, he gave an example as follows:
type header struct { rt http.RoundTripper v map[string]string}func (h header) RoundTrip(r *http.Request) *http.Response { for k, v := range h.v { r.Header.Set(k,v) } return h.rt.RoundTrip(r)}
With interface, we can implement the dynamic dispatch in a similar way.
3. Non-intrusive
Francesc also mentions the non-invasive nature of interface. What is an intrusive? For example, Java's interface implementation needs to display the declaration.
public class MyWriter implements io.Writer {}
This means that if you want to implement multiple interface that need to be written many times, and the package dependencies need to be managed. Dependency is evil. For example, I want to implement the IO packet inside the Reader,writer,readwriter interface, the code can be written as follows.
type MyIO struct {}func (io *MyIO) Read(p []byte) (n int, err error) {...}func (io *MyIO) Write(p []byte) (n int, err error) {...}// io packagetype Reader interface { Read(p []byte) (n int, err error)}type Writer interface { Write(p []byte) (n int, err error)}type ReadWriter interface { Reader Writer}
This kind of writing is really convenient, and it is not necessary to display the import IO package,interface the underlying implementation of the dynamic detection. This will also introduce some questions:
Performance degradation. Using interface as a function parameter, runtime dynamically determines the behavior. Using a struct as a parameter, the compilation period can be determined.
Do not know which interface the struct implements. This problem can be solved by using the Guru tool.
In conclusion, this non-intrusive implementation of the Golang interface is really hard to say whether it is good or bad. But it is certain that the code is easier for developers to write.
4. Interface Type Assertion
Interface like other types of conversions are generally called assertions, for instance.
func do(v interface{}) { n := v.(int) // might panic}
The downside of this writing is that once the assertion fails, the program will panic. One way to avoid panic is to use type assertion.
func do(v interface{}) { n, ok := v.(int) if !ok { // 断言失败处理 }}
For interface operations can be handled using the reflect package, about the principle of reflect package and use can refer to my article.
5. Summary
Interface is an important feature of Golang, but this is at the cost of runtime, which means a loss of performance (about the interface of the underlying implementation and then the time to write again). Aside from the performance (in reality the program developed using Golang 99% performance is not a problem), interface for how to design our code does give a good thought.
6. Reference
1. [Golang "Generic Programming"] (http://legendtkl.com/2015/11/25/go-generic-programming/)
2. [Talk about the interface and reflect of Golang] (http://legendtkl.com/2015/11/28/go-interface-reflect/)
3. [Understanding Golang Interface (Gopher China)-youtube] (https://www.youtube.com/watch?v=F4wUrj6pmSI&t=2319s)
4. [Understanding Golang Interface (Gopher China)-slide] (https://github.com/gopherchina/conference/blob/master/2017 /1.4%20interface.presented.pdf)