概要
Goでは以下のようなブランク識別子(アンダースコア)を使ったimport
を見かけることがあります。
import (
_ "foo"
)
公式リファレンスには、
An import declaration declares a dependency relation between the importing and imported package. It is illegal for a package to import itself, directly or indirectly, or to directly import a package without referring to any of its exported identifiers. To import a package solely for its side-effects (initialization), use the blank identifier as explicit package name:
と書いてありますが、よくわかりません。
実際にブランク識別子を使ったimport
を活用して理解を深めたいと思います。
作るもの
いろんな言語で挨拶をするパッケージgreet
を作成します。
import
によって対応する言語を増やせるようにします。
ソースコード
以下のような構成です
.
├── greet
│ ├── english
│ │ └── english.go
│ ├── greet.go
│ └── japanese
│ └── japanese.go
└── main.go
greet
パッケージの中心となるgreet.go
では、挨拶をする関数Greet()
とパッケージを拡張するための関数Register()
を用意します。
Greet()
は対応する全ての言語の挨拶を表示します。対応する言語がない場合には???
を表示します。
package greet
import (
"fmt"
)
var messages []string
func Register(m string) {
messages = append(messages, m)
}
func Greet() {
if len(messages) == 0 {
fmt.Println("???")
} else {
for _, m := range messages {
fmt.Println(m)
}
}
}
次はgreet
を拡張するためのパッケージjapanese
とenglish
です。
これらのパッケージでは、init()
内でRegister()
関数を呼び出して、greet
パッケージを拡張します。
init()
関数は特別な関数で、main()
関数より前に自動で実行されます。
package japanese
import (
".."
)
func init() {
greet.Register("こんにちは")
}
package japanese
import (
".."
)
func init() {
greet.Register("Hello")
}
最後にmain()
関数ですが、まだimport
によるパッケージの拡張は行っていません。
package main
import (
"./greet"
)
func main() {
greet.Greet()
}
実行する
まずは、このまま実行してみます。
$ go run main.go
???
greet
はどの言語にも対応していないので挨拶ができません。
main.go
のimport
を以下のように変更し、再度実行してみます。
...
import (
"./greet"
_ "./greet/japanese"
)
...
$ go run main.go
こんにちは
japanese
をimport
したことにより、日本語の挨拶が追加されました。さらに、english
もimport
してみます。
...
import (
"./greet"
_ "./greet/english"
_ "./greet/japanese"
)
...
$ go run main.go
Hello
こんにちは
日本語と英語の両方に対応しています。
init()
内でRegister()
を呼び出しているパッケージをimport
することによってgreet
が拡張されていることを確認できました。
まとめ
import
によってパッケージを拡張できるような仕組みを取り入れました。
今回は対象としたパッケージがシンプルだったので、あまり問題はありませんでしたが、実際には拡張される順番がバラバラでもユーザの思い通りに動作するようにしなければならないので、もっと難しくなると思います。
より実践的な使い方に興味がある方は標準ライブラリのimage
等を見ればいいと思います。image.Decode()
でpng形式の画像を読み込めるようにするために、image/png
をimport
してimage
を拡張するような仕組みになっています。