Methods can be defined for any named type that is not a pointer or an interface; the receiver does not have to be a struct. node
除了指针和接口(interface) 能够为任意自定义的类型定义方法 并且这个自定义类型能够不是结构体struct web
In the discussion of slices above, we wrote an Append function. We can define it as a method on slices instead. To do this, we first declare a named type to which we can bind the method, and then make the receiver for the method a value of that type. 算法
在讲slice时 咱们写了一个Append函数 当时咱们是把须要修改的slice以参数形式传递给函数Append 咱们也能够为slice定义方法 可是须要为slice定义别名 而且定义方法的做用对象为该类型 这样能够直接经过slice.Append来给slice添加元素了: express
type ByteSlice []byte // 起别名 func (slice ByteSlice) Append(data []byte) []byte { // Body exactly the same as above }
This still requires the method to return the updated slice. We can eliminate that clumsiness by redefining the method to take a pointer to a ByteSlice as its receiver, so the method can overwrite the caller's slice. 数组
上面代码给ByteSlice定义了方法 但仍是须要返回修改过的slice 为了不来回地传递slice 咱们能够为ByteSlice的指针类型定义方法: 安全
func (p *ByteSlice) Append(data []byte) { slice := *p // Body as above, without the return. *p = slice }
In fact, we can do even better. If we modify our function so it looks like a standard Write method, like this, 服务器
固然了 这里仍是有改进空间的 咱们能够实现io.Writer接口: app
func (p *ByteSlice) Write(data []byte) (n int, err error) { slice := *p // Again as above. *p = slice return len(data), nil }
then the type *ByteSlice satisfies the standard interface io.Writer, which is handy. For instance, we can print into one. ide
实现了io.Writer接口后 咱们能够这样使用它: 函数
var b ByteSlice fmt.Fprintf(&b, "This hour has %d days\n", 7)
We pass the address of a ByteSlice because only *ByteSlice satisfies io.Writer. The rule about pointers vs. values for receivers is that value methods can be invoked on pointers and values, but pointer methods can only be invoked on pointers. This is because pointer methods can modify the receiver; invoking them on a copy of the value would cause those modifications to be discarded.
必须传递指针 这样才能知足io.Writer接口 在定义方法时 若是方法做用对象是某类型的值 则它能够经过该类型值 或者 该类型指针来调用 可是 定义方法时 做用对象是某类型指针 那么只有经过该类型指针才能触发这方法调用
By the way, the idea of using Write on a slice of bytes is implemented by bytes.Buffer.
bytes slice已经实现了Writer接口 参看bytes.Buffer
Interfaces in Go provide a way to specify the behavior of an object: if something can do this, then it can be used here. We've seen a couple of simple examples already; custom printers can be implemented by a String method while Fprintf can generate output to anything with a Write method. Interfaces with only one or two methods are common in Go code, and are usually given a name derived from the method, such as io.Writer for something that implements Write.
Go能够经过接口来得到面向对象的能力 好比 经过给类型定义String方法 就能够自定义其输出的格式 Go中常常能够看到只定义了一两个方法的接口 这些接口的名字和它定义的方法相关 好比 io.Writer接口 实现了Write方法 而接口名为io.Writer
A type can implement multiple interfaces. For instance, a collection can be sorted by the routines in package sort if it implements sort.Interface, which contains Len(),Less(i, j int) bool, and Swap(i, j int), and it could also have a custom formatter. In this contrived example Sequence satisfies both.
一个类型同时能够实现多个接口 好比 要给数组排序 若是实现了sort包中的sort.Interface接口 它就能够直接使用sort包的相关函数 同时也能够实现String函数 定制输出的格式
type Sequence []int // Methods required by sort.Interface.
func (s Sequence) Len() int {
return len(s)
}
func (s Sequence) Less(i, j int) bool {
return s[i] < s[j]
}
func (s Sequence) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
} // Method for printing - sorts the elements before printing.
func (s Sequence) String() string { sort.Sort(s) str := "[" for i, elem := range s { if i > 0 { str += " " } str += fmt.Sprint(elem) } return str + "]" }
The String method of Sequence is recreating the work that Sprint already does for slices. We can share the effort if we convert the Sequence to a plain []int before callingSprint.
上面例子中String方法重复了Sprint的工做 Sprint已经为slice定义了输出方法 咱们能够把Sequence类型转换成[]int 而后直接使用Sprint来输出
func (s Sequence) String() string { sort.Sort(s) return fmt.Sprint([]int(s)) }
The conversion causes s to be treated as an ordinary slice and therefore receive the default formatting. Without the conversion, Sprint would find the String method of Sequence and recur indefinitely. Because the two types (Sequence and []int) are the same if we ignore the type name, it's legal to convert between them. The conversion doesn't create a new value, it just temporarily acts as though the existing value has a new type. (There are other legal conversions, such as from integer to floating point, that do create a new value.)
上述代码中的类型转换致使s被当成是普通的slice来处理 从而在输出时采用默认的格式 若是不作转换 Sprint会去调用Sequence的String方法 而后就进入死循环了 Sequence和[]int除了名字不一样 其它都同样 因此在它们之间作转换是可行的 并且是安全的 转换操做并无建立新的值 它只会临时地把当前的类型当成是另外一种类型来处理罢了
It's an idiom in Go programs to convert the type of an expression to access a different set of methods. As an example, we could use the existing type sort.IntSlice to reduce the entire example to this:
Go中 方法和类型是绑定在一块儿的 因此能够经过类型转换来使用其它类型的方法 请看下面这个例子 咱们可使用sort.IntSlice方法 把上面排序和输出的代码精简为:
type Sequence []int // Method for printing - sorts the elements before printing func (s Sequence) String() string { sort.IntSlice(s).Sort() return fmt.Sprint([]int(s)) }
Now, instead of having Sequence implement multiple interfaces (sorting and printing), we're using the ability of a data item to be converted to multiple types (Sequence,sort.IntSlice and []int), each of which does some part of the job. That's more unusual in practice but can be effective.
这里例子中 咱们不须要本身实现多个接口 好比 排序和输出 咱们能够利用类型转换来使用其它类型的方法
If a type exists only to implement an interface and has no exported methods beyond that interface, there is no need to export the type itself. Exporting just the interface makes it clear that it's the behavior that matters, not the implementation, and that other implementations with different properties can mirror the behavior of the original type. It also avoids the need to repeat the documentation on every instance of a common method.
若是定义一个类型 只为了去实现某个接口 而且除了这个接口中定义的方法外 并不须要把其它的方法导出 那么能够只导出接口 只导出接口 能够强调了接口中定义的函数行为 而不是具体的实现 同时这样作的额外好处是 不须要重复地写文档
In such cases, the constructor should return an interface value rather than the implementing type. As an example, in the hash libraries both crc32.NewIEEE and adler32.New return the interface type hash.Hash32. Substituting the CRC-32 algorithm for Adler-32 in a Go program requires only changing the constructor call; the rest of the code is unaffected by the change of algorithm.
这样的话 构造函数须要返回接口 而不是实现接口的类型 举个例子吧 哈希库crc32.NewIEEE和adler32.New都返回接口类型hash.Hash32 若是要用Adler32来替换CRC32算法 只须要调用另外一个构造函数就能够了 其它的代码都不须要改动
A similar approach allows the streaming cipher algorithms in the various crypto packages to be separated from the block ciphers they chain together. The Block interface in the crypto/cipher package specifies the behavior of a block cipher, which provides encryption of a single block of data. Then, by analogy with the bufio package, cipher packages that implement this interface can be used to construct streaming ciphers, represented by the Stream interface, without knowing the details of the block encryption.
可使用相似的方法 把流加密算法从各类加密包的块加密算法中分离出来 crypto/cipher包中的Block接口 定义了block加密算法的行为(加密单个数据块的方法)和bufio包相似 cipher包能够实现这个接口来引导流加密算法(Stream接口)注:这里须要看一下加密算法 大部分忘记了
The crypto/cipher interfaces look like this:
crypto/cipher接口长得像下面这个样子
type Block interface { BlockSize() int Encrypt(src, dst []byte) Decrypt(src, dst []byte) } type Stream interface { XORKeyStream(dst, src []byte) }
Here's the definition of the counter mode (CTR) stream, which turns a block cipher into a streaming cipher; notice that the block cipher's details are abstracted away:
下面这段代码是计数模式stream的定义 它把block cipher转换成stream cipher
// NewCTR returns a Stream that encrypts/decrypts using the given Block in // counter mode. The length of iv must be the same as the Block's block size. func NewCTR(block Block, iv []byte) Stream
NewCTR applies not just to one specific encryption algorithm and data source but to any implementation of the Block interface and any Stream. Because they return interface values, replacing CTR encryption with other encryption modes is a localized change. The constructor calls must be edited, but because the surrounding code must treat the result only as a Stream, it won't notice the difference.
NewCTR能够用在任何实现了Block或者Stream接口的类型上 因为返回的是接口值 替换CRT加密算法只须要在一个地方改动就能够了
Since almost anything can have methods attached, almost anything can satisfy an interface. One illustrative example is in the http package, which defines the Handler interface. Any object that implements Handler can serve HTTP requests.
任意类型均可以定义方法 http包就是一个很好的例子 它定义了Handler接口 任何实现了Handler接口的对象 均可以处理HTTP请求
type Handler interface { ServeHTTP(ResponseWriter, *Request) }
ResponseWriter is itself an interface that provides access to the methods needed to return the response to the client. Those methods include the standard Write method, so an http.ResponseWriter can be used wherever an io.Writer can be used. Request is a struct containing a parsed representation of the request from the client.
ResponseWriter自己就是一个接口 它提供了响应客户请求的方法 这些方法包括标准的Writer方法 因此任何可使用io.Writer的地方均可以使用http.ResponseWriter Request是一个结构体 它包含了已经解析过的HTTP请求
For brevity, let's ignore POSTs and assume HTTP requests are always GETs; that simplification does not affect the way the handlers are set up. Here's a trivial but complete implementation of a handler to count the number of times the page is visited.
假设须要处理的HTTP请求只有GET方法 这个假设并不会影响http请求处理函数的实现 下面这段代码很简单 可是它能够处理http请求 记录页面被访问的次数
// Simple counter server. type Counter struct { n int } func (ctr *Counter) ServeHTTP(w http.ResponseWriter, req *http.Request) { ctr.n++ fmt.Fprintf(w, "counter = %d\n", ctr.n) }
(Keeping with our theme, note how Fprintf can print to an http.ResponseWriter.) For reference, here's how to attach such a server to a node on the URL tree.
下面这段代码给把/counter映射到特定咱们刚定义的Handler上:
import "net/http" ... ctr := new(Counter) http.Handle("/counter", ctr)
But why make Counter a struct? An integer is all that's needed. (The receiver needs to be a pointer so the increment is visible to the caller.)
为何须要把Counter定义成结构体呢 咱们只须要一个整数
// Simpler counter server. type Counter int func (ctr *Counter) ServeHTTP(w http.ResponseWriter, req *http.Request) { *ctr++ fmt.Fprintf(w, "counter = %d\n", *ctr) }
What if your program has some internal state that needs to be notified that a page has been visited? Tie a channel to the web page.
某个页面被访问了 而你的程序有些内部状态须要知道这个动做 你能够经过channel来实现:
// A channel that sends a notification on each visit. // (Probably want the channel to be buffered.) type Chan chan *http.Request func (ch Chan) ServeHTTP(w http.ResponseWriter, req *http.Request) { ch <- req fmt.Fprint(w, "notification sent") }
Finally, let's say we wanted to present on /args the arguments used when invoking the server binary. It's easy to write a function to print the arguments.
若是咱们想在访问http://host/args时 给出启动server的参数 得到命令参数的函数原型以下:
func ArgServer() { for _, s := range os.Args { fmt.Println(s) } }
How do we turn that into an HTTP server? We could make ArgServer a method of some type whose value we ignore, but there's a cleaner way. Since we can define a method for any type except pointers and interfaces, we can write a method for a function. The http package contains this code:
可是如何让它处理HTTP请求呢 有中作法就是 随便定义个类型 好比是空的结构体 而后像上面那段代码那样实现Handler接口 可是 以前咱们已经知道 能够给除了指针和接口外的任意类型定义方法 函数也是一种类型 咱们能够直接给函数定义方法 下面这段代码能够在http包里找到:
// The HandlerFunc type is an adapter to allow the use of // ordinary functions as HTTP handlers. If f is a function // with the appropriate signature, HandlerFunc(f) is a // Handler object that calls f. type HandlerFunc func(ResponseWriter, *Request) // ServeHTTP calls f(c, req). func (f HandlerFunc) ServeHTTP(w ResponseWriter, req *Request) { f(w, req) }
HandlerFunc是个适配器 可让普通的函数来处理HTTP请求 若是f是一个函数 那么HandlerFunc(f)则是Handler对象 这个对象会调用f
HandlerFunc is a type with a method, ServeHTTP, so values of that type can serve HTTP requests. Look at the implementation of the method: the receiver is a function, f, and the method calls f. That may seem odd but it's not that different from, say, the receiver being a channel and the method sending on the channel.
HandlerFunc是一个函数类型 该类型的值能够处理http请求 看一下它的实现 它做用于函数f 而后在方法里调用f函数 这看起来有点奇怪 可是本质上和其它的类型方法是同样的
To make ArgServer into an HTTP server, we first modify it to have the right signature.
把ArgServer改写成HTTP服务器 咱们首先得改一下它的函数签名 否则HandlerFunc适配不了啊
// Argument server. func ArgServer(w http.ResponseWriter, req *http.Request) { for _, s := range os.Args { fmt.Fprintln(w, s) } }
ArgServer now has same signature as HandlerFunc, so it can be converted to that type to access its methods, just as we converted Sequence to IntSlice to accessIntSlice.Sort. The code to set it up is concise:
改写后 ArgServer的签名和HandlerFunc同样了 能够把它转换成HandlerFunc类型 来使用HandlerFunc的方法:
http.Handle("/args", http.HandlerFunc(ArgServer))
When someone visits the page /args, the handler installed at that page has value ArgServer and type HandlerFunc. The HTTP server will invoke the method ServeHTTP of that type, with ArgServer as the receiver, which will in turn call ArgServer (via the invocation f(c, req) inside HandlerFunc.ServeHTTP). The arguments will then be displayed.
当访问http://host/args时 处理这个URL的handler的值就是ArgServer 类型是HandlerFunc HTTP服务器会调用HandlerFunc类型的ServerHTTP方法
In this section we have made an HTTP server from a struct, an integer, a channel, and a function, all because interfaces are just sets of methods, which can be defined for (almost) any type.
这节咱们用结构体 整数 channel和函数 写了一个HTTP服务器 能够把这些整合起来的缘由就是 接口是一组方法 几乎能够给任何类型定义方法