GolangでLombokを感じてみよう

GolangでLombokを感じてみよう

2023年10月24日

Lombokとは?

Lombokは、Javagettersetterなどの繰り返しのコードを減らすためのライブラリです。

Go言語では特性上、Getter、Setter、コンストラクタの作成が非常に面倒です。

Javaにはこのような作業を自動で行ってくれるライブラリが存在します。

例えば、以下のような構造体にコンストラクタを作成すると仮定してみましょう。

type User struct {
    Name string
    Age int
}

おそらくほとんどの人が次のようにコンストラクタを作成するでしょう。

func NewUser(name string, age int) User {
    return User{
        Name: name,
        Age: age,
    }
}

Javaではコンストラクタの生成を通常このように行います。

public class User {
    private String name;
    private int age;
    
    public User(String name, int age){
        this.name = name;
        this.age = age;
    } 
}

このような作業を自動で行ってくれるライブラリがLombokです。 Lombokを使用すると次のように使うことができます。

import lombok.AllArgsConstructor;

@AllArgsConstructor
public class User {
    private String name;
    private int age;
}

このように開発者にコンストラクタ生成などの繰り返し作業やヒューマンエラーを減らしてくれるライブラリです。

Gombok

Go言語の特性上、ボイラープレートが非常に多く、このようなコードを書き続けると、実際のビジネスコードを書く部分に集中しにくくなります。(単に私が怠け者かもしれませんが…) どうやって自動化するかを考えているうちに、会社でうまく活用できるようにlombokとgoを組み合わせたgombokというコードジェネレーターライブラリを作ってみました。

インストール

簡単にinstallコマンドでインストールできます。

 $ go install github.com/YangTaeyoung/gombok@v1.1.0

使用方法

使用したい構造体にコメントとしてLombokで使用していたアノテーションをそのまま記述します。

// @AllArgsConstructor
// @Builder
// @Getter
// @Setter
type User struct {
    Name string
    Age int
}

そして次のように実行します。特にオプションを指定する必要はなく、このコマンドを実行すると{ファイル名}_gombok.goというファイルが生成されます。

$ gombok

> main.go
> 2023/10/24 23:25:39 Found @AllArgsConstructor in User
> 2023/10/24 23:25:39 Found @Builder in User
> 2023/10/24 23:25:39 Found @Getter in User
> 2023/10/24 23:25:39 Found @Setter in User

このファイルを開くと、次のようにコンストラクタ、ビルダー、Getter、Setterが生成されたことを確認できます。

// Code generated by gombok. DO NOT EDIT.
package main

// NewUserWithAllArgs
func NewUserWithAllArgs(name string, age int) User {
	return User{
		Name: name,
		Age:  age,
	}
}

// UserBuilder
// a builder for User
type UserBuilder struct {
	target *User
}

// WithName
// sets the Name field of the target User
func (ub UserBuilder) WithName(name string) UserBuilder {
	ub.target.Name = name

	return ub
}

// WithAge
// sets the Age field of the target User
func (ub UserBuilder) WithAge(age int) UserBuilder {
	ub.target.Age = age

	return ub
}

// Build
// constructs a User from the builder
func (ub UserBuilder) Build() User {
	return *ub.target
}

// NewUserBuilder
// creates a new builder instance for User
func NewUserBuilder() UserBuilder {
	return UserBuilder{target: &User{}}
}

// GetName
func (u *User) GetName() string {
	return u.Name
}

// GetAge
func (u *User) GetAge() int {
	return u.Age
}

// SetName
func (u *User) SetName(name string) {
	u.Name = name
}

// SetAge
func (u *User) SetAge(age int) {
	u.Age = age
}

あとは単純に生成されたレシーバー関数を使用したい場所で使用すれば良いです。

// @AllArgsConstructor
// @Builder
// @Getter
// @Setter
type User struct {
	Name string
	Age  int
}

func main() {
	user := NewUserBuilder().WithAge(10).WithName("John").Build()

	fmt.Println(user.GetName(), user.GetAge())

	user.SetName("Jane")
	user.SetAge(20)

	fmt.Println(user.GetName(), user.GetAge())

	user2 := NewUserWithAllArgs("giraffe", 27)

	fmt.Println(user2.GetName(), user2.GetAge())
}

結果

John 10
Jane 20
giraffe 27