From: Rob Pike Date: Thu, 15 Oct 2009 06:03:08 +0000 (-0700) Subject: interfaces and methods. X-Git-Tag: weekly.2009-11-06~306 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=2119b3672db42e596e468a2516cf2e61729e683b;p=gostls13.git interfaces and methods. R=rsc DELTA=160 (143 added, 5 deleted, 12 changed) OCL=35748 CL=35758 --- diff --git a/doc/effective_go.html b/doc/effective_go.html index 11944183d4..7fb605d394 100644 --- a/doc/effective_go.html +++ b/doc/effective_go.html @@ -310,11 +310,12 @@ which is a clear, concise name. Moreover, because imported entities are always addressed with their package name, bufio.Reader does not conflict with io.Reader. -Similarly, the constructor for vector.Vector -would normally be called NewVector but since +Similarly, the function to make new instances of vector.Vector +—which is the definition of a constructor in Go—would +normally be called NewVector but since Vector is the only type exported by the package, and since the -package is called vector, it's called just New, -which clients of the package see as vector.New. +package is called vector, it's called just New. +Clients of the package see that as vector.New. Use the package structure to help you choose good names.

@@ -1372,7 +1373,7 @@ By the way, the idea of using Write on a slice of bytes is implemented by bytes.Buffer.

-

Interfaces and the interplay of types

+

Interfaces and other types

Interfaces

@@ -1382,7 +1383,7 @@ object: if something can do this, then it can be used 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, and are +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.

@@ -1477,10 +1478,11 @@ 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 publish the type itself. -Publishing just the interface makes it easy for -other implementations with different properties -to mirror the job of the original type. +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.

@@ -1502,7 +1504,7 @@ By analogy to the bufio package, they wrap a Cipher interface and they return hash.Hash, io.Reader, or io.Writer -interface values, not direct implementations. +interface values, not specific implementations.

The interface to crypto/block includes: @@ -1534,6 +1536,147 @@ calls must be edited, but because the code must treat the result only as an io.Reader, it won't notice the difference.

+

Interfaces and methods

+

+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. +

+
+type Handler interface {
+	ServeHTTP(*Conn, *Request);
+}
+
+

+For brevity, let's ignore POSTs and assume HTTP requests are always +GETs; that simplification does not affect the way the handlers are +made. Here's a trivial but complete implementation of a handler to +count the number of times the +page is visited. +

+
+// Simple counter server.
+type Counter struct {
+	n int;
+}
+
+func (ctr *Counter) ServeHTTP(c *http.Conn, req *http.Request) {
+	ctr.n++;
+	fmt.Fprintf(c, "counter = %d\n", ctr.n);
+}
+
+

+(Keeping with our theme, note how Fprintf can print to an HTTP connection.) +For reference, here's how to set up such a server. +

+import "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.) +

+
+// Simpler counter server.
+type Counter int
+
+func (ctr *Counter) ServeHTTP(c *http.Conn, req *http.Request) {
+	ctr++;
+	fmt.Fprintf(c, "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. +

+
+// A channel that sends a notification on each visit.
+// (Probably want the channel to be buffered.)
+type Chan chan int
+
+func (ch Chan) ServeHTTP(c *http.Conn, req *http.Request) {
+	ch <- 1;
+	fmt.Fprint(c, "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: +

+
+func ArgServer() {
+	for i, 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 write a method for (almost) any type, we can write a method +for a function. +The http package contains this code: +

+
+// 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(*Conn, *Request)
+
+// ServeHTTP calls f(c, req).
+func (f HandlerFunc) ServeHTTP(c *Conn, req *Request) {
+	f(c, req);
+}
+
+

+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 no different from, say, +the receiver being a channel and the method sending on the channel. +

+

+To make ArgServer into an HTTP server, we first give it the right +signature. +

+
+// Argument server.
+func ArgServer(c *http.Conn, req *http.Request) {
+	for i, s := range os.Args {
+		fmt.Fprintln(c, s);
+	}
+}
+
+

+ArgServer has same signature as HandlerFunc, +so the function can be converted to that type to access its methods, +just as we converted Sequence to []int earlier. +The code to set it up is short: +

+
+http.Handle("/args", http.HandlerFunc(ArgServer));
+
+

+When someone visits the page /args, +the handler installed at that page has type +HandlerFunc and value ArgServer. +The HTTP server will invoke the method ServeHTTP +of that type, with that receiver, which will in turn call +ArgServer (via the invocation f(c, req) +inside HandlerFunc.ServeHTTP) and the arguments +will be displayed. +

+

+In summary, 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. +

+

Errors

@@ -1604,8 +1747,6 @@ for try := 0; try < 2; try++ { } -

Testing

-

More to come

+((link to go code search for 'for.*range' here))

Use reflect.DeepEqual to compare complex values