【Go】依存性の注入をやってみる

Go

はじめに

PHPやLaravelで依存性の注入は書いたことがあるのでイメージできますが、Goでは書いたことがないので、書いてみました。

コード

Loggerインターフェイスで持たせたいメソッドを定義しています。

そして、そのインターフェイスを満たす実装をしているのが、ConsoleLoggerとFileLogger構造体です。

ConsoleLoggerはコンソールに出力するだけ。

FileLoggerは指定ファイルにログを書き込みます。

Service構造体にはLoggerインターフェイスを持たせて、ConsoleLoggerとFileLoggerのどちらでも入れられるようにしています。

実際にはコンストラクタとしてNewServiceメソッドで注入しています。

こうすることで、Loggerインターフェイスを実装しているものなら好きなようにコンストラクタで差し替えることができます。

インターフェイスを満たすモックを作ることで簡単にテストすることも可能になります。

package main

import (
	"fmt"
	"os"
)

type Logger interface {
	Info(message string)
	Warn(message string)
	Error(message string)
	Debug(message string)
	Fatal(message string)
}

type ConsoleLogger struct{}

func (c *ConsoleLogger) Info(message string) {
	fmt.Println("Info: " + message)
}

func (c *ConsoleLogger) Warn(message string) {
	fmt.Println("Warn: " + message)
}

func (c *ConsoleLogger) Error(message string) {
	fmt.Println("Error: " + message)
}

func (c *ConsoleLogger) Debug(message string) {
	fmt.Println("Debug: " + message)
}

func (c *ConsoleLogger) Fatal(message string) {
	fmt.Println("Fatal: " + message)
}

type FileLogger struct {
	filename string
}

func (f *FileLogger) Info(message string) {
	f.Log("Info: " + message)
}

func (f *FileLogger) Warn(message string) {
	f.Log("Warn: " + message)
}

func (f *FileLogger) Error(message string) {
	f.Log("Error: " + message)
}

func (f *FileLogger) Debug(message string) {
	f.Log("Debug: " + message)
}

func (f *FileLogger) Fatal(message string) {
	f.Log("Fatal: " + message)
}

func (f *FileLogger) Log(message string) {
	file, err := os.OpenFile(f.filename, os.O_RDWR|os.O_APPEND, 0644)
	if err != nil {
		file, err = os.Create(f.filename)
		if err != nil {
			fmt.Println(err)
			return
		}
	}
	defer file.Close()

	_, err = file.WriteString(message + "\n")
	if err != nil {
		fmt.Println(err)
		return
	}

	fmt.Printf("Logging to file %s: %s\n", f.filename, message)
}

type Service struct {
	logger Logger
}

func NewService(logger Logger) *Service {
	return &Service{logger: logger}
}

func (s Service) LoggingAll() {
	s.logger.Info("Info message")
	s.logger.Warn("Warn message")
	s.logger.Error("Error message")
	s.logger.Debug("Debug message")
	s.logger.Fatal("Fatal message")
}

func main() {
	// ConsoleLoggerを使用
	consoleLogger := &ConsoleLogger{}
	service1 := NewService(consoleLogger)
	service1.LoggingAll()

	// FileLoggerを使用
	fileLogger := &FileLogger{filename: "log.txt"}
	service2 := NewService(fileLogger)
	service2.LoggingAll()
}

動かしてみる

go run main.go

Info: Info message
Warn: Warn message
Error: Error message
Debug: Debug message
Fatal: Fatal message

Logging to file log.txt: Info: Info message
Logging to file log.txt: Warn: Warn message
Logging to file log.txt: Error: Error message
Logging to file log.txt: Debug: Debug message
Logging to file log.txt: Fatal: Fatal message