interface
Goのinterfaceは,メソッドの集合を定義する型です.ある型がinterfaceで定義されたすべてのメソッドを実装していれば,その型はそのinterfaceを満たすと言います.
// グラディエーターインターフェース
type Gladiator interface {
Attack() int // 攻撃力を返す
GetWeapon() string // 武器名を返す
}
// 剣使いの構造体
type Swordsman struct {
name string
}
func (s Swordsman) Attack() int {
return 2
}
func (s Swordsman) GetWeapon() string {
return "剣"
}
// 斧使いの構造体
type Axeman struct {
name string
}
func (a Axeman) Attack() int {
return 3
}
func (a Axeman) GetWeapon() string {
return "斧"
}
// 闘技場での戦い
func arenaFight(g Gladiator) {
fmt.Printf("%s使いの攻撃!ダメージ: %d\n", g.GetWeapon(), g.Attack())
}
// 使用例
func main() {
swordsman := Swordsman{name: "ケンタロウ"}
axeman := Axeman{name: "オノマル"}
arenaFight(swordsman) // 剣使いの攻撃!ダメージ: 2
arenaFight(axeman) // 斧使いの攻撃!ダメージ: 3
}
SwordsmanやAxemanは明示的にGradiatorを実装しているわけではありませんが,Attack()
とGetWeapon()
メソッドを持っているのでinterfaceを満たしていると言えます.
Everything is a file
Unix系のOSにおいて,「Everything is a file」という哲学があります.これは,システム内のあらゆるリソースを統一的な方法で扱うという考え方です.より具体的には,通常のファイルだけでなく,以下のようなものも全て「ファイル」として扱います.
- ディレクトリ (/home/user)
- デバイス (/dev/sda1, /dev/null)
- プロセス情報 (/proc/1234/status)
- ネットワークソケット
- パイプ
これらは全て同じシステムコールread
とwrite
でアクセスできます.
# 通常のファイルを読む
cat file.txt
# ネットワークからのレスポンスを読み込む
curl https://example.com
# 読み込んだ内容をファイルに保存(読み書き)
curl https://example.com > index.html
この統一性により.プログラマーは「読み書きできるもの」として単純に考えることができます.
ioパッケージ
Goのioパッケージはこの「Everything is a file」の思想を体現したものです(多分).
その代表がReader
インターフェースとWriter
インターフェースです.
io.Reader
type Reader interface {
Read(p []byte) (n int, err error)
}
「読み込み可能なもの」です.Read
は,与えられたバイトスライスp
に最大len(p)
バイトを読み込み,実際に読み込んだバイト数n
と発生したエラーerr
を返します.ファイルの終端に達した場合,Read
は0, io.EOF
を返します.
io.Writer
type Writer interface {
Write(p []byte) (n int, err error)
}
「書き込み可能なもの」です.Write
は,与えられたバイトスライスp
のデータを書き込み先に書き込み,実際に書き込んだバイト数n
と発生したエラーerr
を返します.
これらのインターフェースは,以下のような多くの型で実装されています.
os.File
(ファイル)bytes.Buffer
(メモリバッファ)net.Conn
(ネットワーク接続)
コード例
以下の関数copyData
は,io.Reader
からio.Writer
へデータをコピーするだけですが,ファイル,ネットワーク,バッファなどあらゆる読み書き対象で使い回すことができます.
package main
import (
"io"
"net/http"
"os"
)
// src(入力)からdst(出力)へコピー
func copyData(dst io.Writer, src io.Reader) {
io.Copy(dst, src)
}
func main() {
// ファイルをコピー
file1, _ := os.Open("input.txt")
file2, _ := os.Create("output.txt")
copyData(file2, file1)
file1.Close()
file2.Close()
// ネットワークからファイルへ
resp, _ := http.Get("https://example.com/data")
file3, _ := os.Create("download.txt")
copyData(file3, resp.Body)
resp.Body.Close()
file3.Close()
}