LoginSignup
12
11

More than 5 years have passed since last update.

importによってパッケージを拡張する方法(プラグイン)

Posted at

概要

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()は対応する全ての言語の挨拶を表示します。対応する言語がない場合には???を表示します。

greet.go
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を拡張するためのパッケージjapaneseenglishです。
これらのパッケージでは、init()内でRegister()関数を呼び出して、greetパッケージを拡張します。
init()関数は特別な関数で、main()関数より前に自動で実行されます。

japanese.go
package japanese

import (
    ".."
)

func init() {
    greet.Register("こんにちは")
}
english.go
package japanese

import (
    ".."
)

func init() {
    greet.Register("Hello")
}

最後にmain()関数ですが、まだimportによるパッケージの拡張は行っていません。

main.go
package main

import (
    "./greet"
)

func main() {
    greet.Greet()
}

実行する

まずは、このまま実行してみます。

$ go run main.go
???

greetはどの言語にも対応していないので挨拶ができません。

main.goimportを以下のように変更し、再度実行してみます。

main.go
...
import (
    "./greet"
    _ "./greet/japanese"
)
...
$ go run main.go
こんにちは

japaneseimportしたことにより、日本語の挨拶が追加されました。さらに、englishimportしてみます。

main.go
...
import (
    "./greet"
    _ "./greet/english"
    _ "./greet/japanese"
)
...
$ go run main.go
Hello
こんにちは

日本語と英語の両方に対応しています。
init()内でRegister()を呼び出しているパッケージをimportすることによってgreetが拡張されていることを確認できました。

まとめ

importによってパッケージを拡張できるような仕組みを取り入れました。

今回は対象としたパッケージがシンプルだったので、あまり問題はありませんでしたが、実際には拡張される順番がバラバラでもユーザの思い通りに動作するようにしなければならないので、もっと難しくなると思います。

より実践的な使い方に興味がある方は標準ライブラリのimage等を見ればいいと思います。image.Decode()でpng形式の画像を読み込めるようにするために、image/pngimportしてimageを拡張するような仕組みになっています。

12
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
11