| <!--{ | |
| "Title": "JSON-RPC: a tale of interfaces" | |
| }--> | |
| <p> | |
| Here we present an example where Go's | |
| <a href="/doc/effective_go.html#interfaces_and_types">interfaces</a> made it | |
| easy to refactor some existing code to make it more flexible and extensible. | |
| Originally, the standard library's <a href="/pkg/net/rpc/">RPC package</a> used | |
| a custom wire format called <a href="/pkg/encoding/gob/">gob</a>. For a | |
| particular application, we wanted to use <a href="/pkg/encoding/json/">JSON</a> | |
| as an alternate wire format. | |
| </p> | |
| <p> | |
| We first defined a pair of interfaces to describe the functionality of the | |
| existing wire format, one for the client, and one for the server (depicted | |
| below). | |
| </p> | |
| <pre> | |
| type ServerCodec interface { | |
| ReadRequestHeader(*Request) error | |
| ReadRequestBody(interface{}) error | |
| WriteResponse(*Response, interface{}) error | |
| Close() error | |
| } | |
| </pre> | |
| <p> | |
| On the server side, we then changed two internal function signatures to accept | |
| the <code>ServerCodec</code> interface instead of our existing | |
| <code>gob.Encoder</code>. Here's one of them: | |
| </p> | |
| <pre> | |
| func sendResponse(sending *sync.Mutex, req *Request, | |
| reply interface{}, enc *gob.Encoder, errmsg string) | |
| </pre> | |
| <p> | |
| became | |
| </p> | |
| <pre> | |
| func sendResponse(sending *sync.Mutex, req *Request, | |
| reply interface{}, enc ServerCodec, errmsg string) | |
| </pre> | |
| <p> | |
| We then wrote a trivial <code>gobServerCodec</code> wrapper to reproduce the | |
| original functionality. From there it is simple to build a | |
| <code>jsonServerCodec</code>. | |
| </p> | |
| <p> | |
| After some similar changes to the client side, this was the full extent of the | |
| work we needed to do on the RPC package. This whole exercise took about 20 | |
| minutes! After tidying up and testing the new code, the | |
| <a href="http://code.google.com/p/go/source/diff?spec=svn9daf796ebf1cae97b2fcf760a4ab682f1f063f29&r=9daf796ebf1cae97b2fcf760a4ab682f1f063f29&format=side&path=/src/pkg/rpc/server.go">final changeset</a> | |
| was submitted. | |
| </p> | |
| <p> | |
| In an inheritance-oriented language like Java or C++, the obvious path would be | |
| to generalize the RPC class, and create JsonRPC and GobRPC subclasses. However, | |
| this approach becomes tricky if you want to make a further generalization | |
| orthogonal to that hierarchy. (For example, if you were to implement an | |
| alternate RPC standard). In our Go package, we took a route that is both | |
| conceptually simpler and requires less code be written or changed. | |
| </p> | |
| <p> | |
| A vital quality for any codebase is maintainability. As needs change, it is | |
| essential to adapt your code easily and cleanly, lest it become unwieldy to work | |
| with. We believe Go's lightweight, composition-oriented type system provides a | |
| means of structuring code that scales. | |
| </p> |