JSON-RPC:インターフェースの物語

JSON-RPC: a tale of interfaces by Andrew Gerrand

ここでは、Goのインターフェースのおかげで、 既存のコードをより柔軟で拡張性の高いものにリファクタリングするのが簡単になった例を紹介します。 もともと標準ライブラリのRPCパッケージは、gobと呼ばれる独自の ワイヤーフォーマットを使っていました。あるアプリケーションのために、 JSONを代替のワイヤーフォーマットとして使いたいと考えました。

まず、既存のワイヤーフォーマットの機能を表す一対のインターフェースを定義しました。一つはクライアント用、 もう一つはサーバー用です(以下に示します)。

type ServerCodec interface {
    ReadRequestHeader(*Request) error
    ReadRequestBody(interface{}) error
    WriteResponse(*Response, interface{}) error
    Close() error
}

サーバー側では、既存の gob.Encoder の代わりに ServerCodec インターフェースを受け取るように、 内部の2つの関数シグネチャを変更しました。そのうちの一つはこちらです。

func sendResponse(sending *sync.Mutex, req *Request,
    reply interface{}, enc *gob.Encoder, errmsg string)

次のようになりました。

func sendResponse(sending *sync.Mutex, req *Request,
    reply interface{}, enc ServerCodec, errmsg string)

続いて、元の機能を再現する単純な gobServerCodec ラッパーを書きました。そこから jsonServerCodec を 構築するのは簡単でした。

クライアント側にも同様の変更を加えたあと、RPCパッケージに対して必要だった作業はこれがすべてでした。 この一連の作業には20分ほどしかかかりませんでした!新しいコードを整理してテストしたあと、 最終的な変更セットを提出しました。

JavaやC++のような継承指向の言語であれば、素直なやり方はRPCクラスを汎化して JsonRPCGobRPC というサブクラスを作ることでしょう。しかし、この階層構造とは直交する形でさらに汎化を進めようとすると、 このアプローチはややこしくなります(たとえば、別のRPC標準を実装しようとした場合です)。 私たちのGoパッケージでは、概念的にもよりシンプルで、書いたり変更したりするコードの量も少なくて済む方法を選びました。

どのようなコードベースであっても、重要な性質の一つが保守性です。要件が変化するにつれて、コードを簡単かつ クリーンに適応させていくことが不可欠であり、そうしなければコードは扱いにくいものになってしまいます。 私たちは、Goの軽量で構成(コンポジション)指向の型システムが、スケールするコードを構造化する手段を 提供していると考えています。

By Andrew Gerrand