2014/05/01

Recent entries from same category

  1. Go言語で Lisp 処理系を作った。
  2. Go 言語のスライス挿入ベンチマーク
  3. Go 言語で変数のシャドウイングを避けたいなら shadow を使おう。
  4. Go 言語の struct の実体を引数で(なるべく)渡せない様にするテクニック
  5. Oracle Cloud Function でしりとりした。

golang が提供するインタフェースの中で代表的な物の使い方をまとめてみる。

io.Reader

type Reader interface {
    Read(p []byte) (n int, err error)
}
ご存じ io.Reader。このシグネチャの Read を実装しておけば golang のありとあらゆる入力機能に対して自分のコードを提供する事が出来る。 例えば永遠に「おっぱい」と言い続ける Reader だと以下の様な実装になる。
package main

import (
    "io"
    "os"
)

var text = []rune("おっぱい")

type OppaiReader struct {
    n int
}

func (r *OppaiReader) Read(p []byte) (interror) {
    in := len(p)
    nw := 0
    for i := 0; i < in; i++ {
        cb := []byte(string(text[r.n%len(text)]))
        if nw+len(cb) > in {
            break
        }
        cbl := len(cb)
        copy(p[nw:nw+cbl], cb)
        nw += cbl
        r.n++
    }
    return nw, nil
}

func main() {
    io.Copy(os.Stdout, &OppaiReader{})
}
ただしこういうことをする場合は、RuneReader を使った方が綺麗かもしれない。

io.Writer

type Writer interface {
    Write(p []byte) (n int, err error)
}
io.Reader のペアとなるインタフェース。同じくこの関数を実装しておけば golang のあらゆる出力機能に対して自分のコードを提供出来る。
例えば出力される文字列を Pascal Case で出力する Writer だと以下のコードになる。
package main

import (
    "fmt"
    "io"
    "os"
)

type pascalCaseWriter struct {
    w    io.Writer
    last byte
}

func (w *pascalCaseWriter) Write(p []byte) (interror) {
    r := 0
    var b[1byte
    for n, _ := range p {
        b[0] = p[n]
        switch w.last {
        case ' ''\t''\r''\n'0:
            if 'a' <= b[0] && b[0<= 'z' {
                b[0-= 32
            }
        }

        nw, err := w.w.Write(b[:])
        if err != nil {
            return r + nw, err
        }
        w.last = b[0]
    }
    return r, nil
}

func NewPascalCaseWriter(w io.Writer) *pascalCaseWriter {
    return &pascalCaseWriter{w, 0}
}

func main() {
    w := NewPascalCaseWriter(os.Stdout)
    fmt.Fprintln(w, "hello world")
}
Close メソッドを提供するインタフェースとなるが、実際には単品で使われる事はなく、Reader かつ Closer、Writer かつ Closer という使われ方になる。

io.Seeker

type Seeker interface {
    Seek(offset int64, whence int) (int64error)
}
名前の通り、指定オフセット位置でシーク出来るインタフェースとなる。これを実装した代表的な物としては os.File がある。

io.ReadWriter

type ReadWriter interface {
    Reader
    Writer
}
Reader でありかつ Writer であるインタフェースで双方向通信を行う為のインタフェース。代表的な実装としては net.Conn がある。

io.WriteCloser

type WriteCloser interface {
    Writer
    Closer
}
こちらは os.Create で作成した os.File が実装するインタフェース。

io.ByteReader

type ByteReader interface {
    ReadByte() (c byte, err error)
}
ある入力から1バイトずつ読み込む事が出来るインタフェース。tail 的な処理を書きたい場合に用いる。

その他、組み合わせで用いる事が出来る io.ReadWriteCloser io.ReadSeeker io.WriteSeeker io.ReadWriteSeeker io.ByteScanner io.ByteWriter io.RuneReader io.RuneScanner がある。

compress/flate/inflate.Reader

type Reader interface {
    io.Reader
    io.ByteReader
}
compress 圧縮の Reader を提供する。圧縮されたファイル等をこれを用いて Read すると等価的に内容が読めるという物。
実際には NewReader を用いて使用する。

database/sql.Driver

type Driver interface {
    Open(name string) (Conn, error)
}
データベースドライバ実装を表すインタフェース。新しいデータベースのバインディングをサポートする場合、まずこのインタフェース実装を書くことになる。

database/sql.Execer

type Execer interface {
    Exec(query string, args []Value) (Result, error)
}
通常は database/sql では Prepare して Exec するのが普通の使い方だが、データベース接続が Execer を実装している場合は、いちいち Prepare 等せずいきなり接続から実行できる事を明示する事が出来る。
ドライバの実装は必須ではない。

database/sql.Queryer

type Queryer interface {
    Query(query string, args []Value) (Rows, error)
}
Execer と同様に、結果を得るクエリの場合 Prepare して Query する必要があるが、データベース接続から直接 Query 出来るドライバである事を明示する事が出来る。
ドライバの実装は必須ではない。

database/sql.ColumnConverter

type ColumnConverter interface {
    ColumnConverter(idx int) ValueConverter
}
golang の database/sql は自動で型変換を行ってはくれるが、ある特殊な型に対して変換を行いたい場合にはこのインタフェースを実装しする。このインタフェースを実装する場合、ValueConverter も実装する必要がある。

database/sql.ValueConverter

type ValueConverter interface {
    ConvertValue(v interface{}) (Value, error)
}
各型に対する変換処理を提供するインタフェース。例えば bool 型から各型への変換を提供する boolType では以下の実装になっている。
func (boolType) ConvertValue(src interface{}) (Value, error) {
    switch s := src.(type) {
    case bool:
        return s, nil
    case string:
        b, err := strconv.ParseBool(s)
        if err != nil {
            return nil, fmt.Errorf("sql/driver: couldn't convert %q into type bool", s)
        }
        return b, nil
    case []byte:
        b, err := strconv.ParseBool(string(s))
        if err != nil {
            return nil, fmt.Errorf("sql/driver: couldn't convert %q into type bool", s)
        }
        return b, nil
    }

    sv := reflect.ValueOf(src)
    switch sv.Kind() {
    case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
        iv := sv.Int()
        if iv == 1 || iv == 0 {
            return iv == 1nil
        }
        return nil, fmt.Errorf("sql/driver: couldn't convert %d into type bool", iv)
    case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
        uv := sv.Uint()
        if uv == 1 || uv == 0 {
            return uv == 1nil
        }
        return nil, fmt.Errorf("sql/driver: couldn't convert %d into type bool", uv)
    }

    return nil, fmt.Errorf("sql/driver: couldn't convert %v (%T) into type bool", src, src)
}
ドライバの実装は必須ではない。

database/sql.Valuer

type Valuer interface {
    Value() (Value, error)
}
golang のデータベースドライバを実装するパッケージに対して golang 側から値を要求する際に用いられるインタフェース。
ドライバの実装は必須ではない。

database/sql.Scanner

type Scanner interface {
    Scan(src interface{}) error
}
rows.Scan に対して自前の実装を入れたい場合にインプリメントする。
ドライバの実装は必須ではない。

binary.ByteOrder

type ByteOrder interface {
    Uint16([]byteuint16
    Uint32([]byteuint32
    Uint64([]byteuint64
    PutUint16([]byteuint16)
    PutUint32([]byteuint32)
    PutUint64([]byteuint64)
    String() string
}
binary.BigEndianbinary.LittleEndian を実装に持つインタフェース。ユーザがさらに実装を追加する事はおそらく無い、BigEndian と LittleEndian どちらでやってくるか分からない処理に対して等価的に処理を行いたい場合には、このインタフェースを引数に取って扱う。

encoding.BinaryMarshaler

type BinaryMarshaler interface {
    MarshalBinary() (data []byte, err error)
}
あるインスタンスがバイナリ列へシリアライズ可能かどうかを示すインタフェース。

binary.BinaryUnmarshaler

type BinaryUnmarshaler interface {
    UnmarshalBinary(data []byteerror
}
あるインスタンスがバイナリ列からデシリアライズ可能かどうかを示すインタフェース。

encoding.TextMarshaler encoding.TextUnmarshaler も同様に、文字列に対する交換インタフェースを提供する。

encoding/gob.GobEncoder

type GobEncoder interface {
    GobEncode() ([]byteerror)
}
golang には標準で Gob というシリアライザブルフォーマットに対するインタフェースが提供されているが、インスタンスに対して Gob フォーマットへの交換が可能かどうかを明示出来る。

encoding/gob.GobDecoder

type GobDecoder interface {
    GobDecode([]byteerror
}
同様に Gob からある型への交換が可能かどうかを明示出来る。

encoding/json.Unmarshaler

type Unmarshaler interface {
    UnmarshalJSON([]byteerror
}
JSON を示すバイト列からある型への交換が可能である事を示す。

encoding/json.Marshaler

type Marshaler interface {
    MarshalJSON() ([]byteerror)
}
Unmarshaler とは逆にバイト列からある型への交換が可能である事を示す。
Marshaler と Unmarshaler の例を以下に示す。
package main

import (
    "encoding/base64"
    "encoding/json"
    "fmt"
    "log"
)

type User struct {
    Name     string
    Password string
}

func (user *User) UnmarshalJSON(p []byteerror {
    var v map[string]interface{}
    err := json.Unmarshal(p, &v)
    if err != nil {
        return err
    }
    for _, f := range []string{"Name""name"} {
        if vv, ok := v[f]; ok {
            user.Name = fmt.Sprint(vv)
            break
        }
    }
    for _, f := range []string{"Password""password""PassWord""passWord"} {
        if vv, ok := v[f]; ok {
            b, err := base64.StdEncoding.DecodeString(fmt.Sprint(vv))
            if err != nil {
                return nil
            }
            user.Password = string(b)
            break
        }
    }
    return nil
}

func (user *User) MarshalJSON() ([]byteerror) {
    return []byte(fmt.Sprintf(`{"Name": %q: "Password": %q}`,
        user.Name, base64.StdEncoding.EncodeToString([]byte(user.Password)))), nil
}

func main() {
    p := &User{"ウルトラマン""henshin"}
    b, err := p.MarshalJSON()
    if err != nil {
        log.Fatal(err)
    }
    // {"Name": "ウルトラマン", "Password": "aGVuc2hpbg=="}
    fmt.Println(string(b))

    err = p.UnmarshalJSON([]byte(`
    {
        "Name": "仮面ライダー",
        "Password": "aGVuc2hpbg=="
    }
    `))
    if err != nil {
        log.Fatal(err)
    }
    // 仮面ライダー henshin
    fmt.Printf("%s : %s\n", p.Name, p.Password)
}
encoding.xml.Unmarshaler encoding.xml.Marshaler も json と同様。

flag.Getter

type Getter interface {
    Value
    Get() interface{}
}
golang の flag パッケージを用いて、プログラム引数から独自の型へ変換を行いたい場合に使う。

fmt.Formatter

type State interface {
    // Write is the function to call to emit formatted output to be printed.
    Write(b []byte) (ret int, err error)
    // Width returns the value of the width option and whether it has been set.
    Width() (wid int, ok bool)
    // Precision returns the value of the precision option and whether it has been set.
    Precision() (prec int, ok bool)

    // Flag reports whether the flag c, a character, has been set.
    Flag(c intbool
}
fmt.Printf と同様の処理を自前実装したい場合に使う。

fmt.Stringer

type Stringer interface {
    String() string
}
実はこのインタフェースが一番使われる事が多い。デバッグではよく fmt.Println を使う事が多いと思うが、これを実装しておくと独自の型が fmt.Println 等に渡った時にどの様に表示されるかを実装する事が出来る。
package main

import (
    "fmt"
)

type Oppai bool

func (o Oppai) String() string {
    if o {
        return "(・)(・)"
    }
    return "(◎)(◎)"
}

func main() {
    var o Oppai

    o = true
    fmt.Println(o) // (・)(・)

    o = false
    fmt.Println(o) // "(◎)(◎)"
}
fmt.GoStringer は同様のインタフェースではあるが、実際には内部処理のみで使われている。

fmt.Scanner

type Scanner interface {
    Scan(state ScanState, verb runeerror
}
fmt.Scan と同様の処理を自前実装したい場合に使う。

image/draw.Quantizer

type Quantizer interface {
    Quantize(p color.Palette, m image.Image) color.Palette
}
独自のカラーパレットを実装したい場合に使用する。gif は 256 色しか出力出来ないのでこれを使って減色処理を行っている。
Drawer はこのパレットを用いて描画を行う際に実装する。

image/jpeg.Reader

type Reader interface {
    io.Reader
    ReadByte() (c byte, err error)
}
jpeg を自前でデコードしたい場合に実装する。

net/http.RoundTripper

type RoundTripper interface {
    RoundTrip(*Request) (*Response, error)
}
http のトランスポート層を抽象化する為に用いられるインタフェース。
デフォルトのタイムアウトやダイアラ、未知のプロキシに対応する場合はこれを実装する事になる。golang の http.Clientfile:// な URL でも Get する事が出来るが、これは内部でスキーマを判定して RoundTripper を切り替える事で実現している。

net/http.Handler

type Handler interface {
    ServeHTTP(ResponseWriter, *Request)
}
ご存じ http.Handler。これを実装しておくと、とりあえず golang 標準のウェブサーバに対するリクエストを処理出来る。
例えば go-uwsgi はこのインタフェースを実装する事で uWSGI インタフェースを実現している。

net/http.ResponseWriter

type ResponseWriter interface {
    Header() Header
    Write([]byte) (interror)
    WriteHeader(int)
}
golang 標準の Web サーバ機能を使う場合にレスポンスを書き込むインタフェース。実際にはこれをユーザ側が実装する事はまずない。主にテスト結果を得る為に用いられる。

net/http.Flusher

内部処理やテストで用いられる。

net/http.Hijacker

type Hijacker interface {
    Hijack() (net.Conn, *bufio.ReadWriter, error)
}
golang の http 通信は大部分が隠ぺいされている。しかし自前で接続を切ってしまったり、https の connect メソッドを実現する場合には都合が悪いインタフェースだ。そこで req.Hijack() を呼び出し、http 以外の通信を行う事が出来る。
websocket パッケージはこれを使って http ネゴシエーションを実現している。

net/http.CloseNotifier

type CloseNotifier interface {
    CloseNotify() <-chan bool
}
ResponseWriter を自前実装した場合、クライアントから接続を切られた事を検知する為に実装するインタフェース。

net.Listener

type Listener interface {
    Accept() (c Conn, err error)
    Close() error
    Addr() Addr
}
自前実装をソケットとして扱わせる為に実装するインタフェース。上記で紹介した go-uwsgi でも自前で Accept を実装する事で http サーバとの間の通信層で uWSGI を喋っている。

sync.Locker

type Locker interface {
        Lock()
        Unlock()
}
sync.Mutex が実装しているが、自前でもロックインタフェースを実装したい場合にインプリメントする。
実装するのは自由だが、インタフェースが取り決められていないと発散するのでそれを定義しているといった所だろうか。

以上、golang の標準パッケージに含まれる代表的なインタフェースを紹介してみた。
golang のコードがエレガントになるかどうかの半分は、このインタフェースにかかっている。ぜひ極めましょう。
Posted at by | Edit


blog comments powered by Disqus