0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【備忘録】ファクトリ関数が役に立った事例

Posted at

ファクトリ関数の解釈

役に立った事例:UUIDの生成とテスト対応

背景

以下はドメイン層における uuid.go のコードである。

// domain/uuid.go
package domain

// UUIDGenerator は UUID を生成するインターフェース
type UUIDGenerator interface {
    NewUUID() (UUID, error)
}

// UUID は UUID を表す値オブジェクト
type UUID struct {
    value string
}

// Value は UUID の値を返す
func (u UUID) Value() string {
    return u.value
}

UUID の生成方法が外部に依存しないよう、UUIDGenerator はインターフェースとして定義している。
インフラ層やテストコードでは、このインターフェースを実装する必要がある。

問題点

テストコード内で以下のように UUIDGenerator をモック実装した。

// モックUUIDGenerator実装
type mockUUIDGenerator struct{}

func (mockUUIDGenerator) NewUUID() (domain.UUID, error) {
    // テスト用の固定UUIDを返す
    return domain.UUID{value: "550e8400-e29b-41d4-a716-446655440000"}, nil
}

しかし、以下のようなエラーが発生した。

usecase/create_account_test.go:40:24: cannot refer to unexported field value in struct literal of type domain.UUID

これは Go 言語のカプセル化(非公開フィールド)のルールにより、外部から value フィールドを直接参照できないことが原因である。
値オブジェクトが「生成時の一貫性保証」という特性を持つ以上、これは自然な振る舞いであるとも言える。

だがこのままでは、ドメイン外から UUID を生成できず、インターフェースの実装が不可能になる。

解決策:ファクトリ関数の導入

この問題は、ファクトリ関数を用いることで解決できる。

修正後の uuid.go

package domain

import "errors"

var (
    ErrInvalidUUID = errors.New("invalid UUID format")
)

// UUIDGenerator は UUID を生成するインターフェース
type UUIDGenerator interface {
    NewUUID() (UUID, error)
}

// UUID は UUID を表す値オブジェクト
type UUID struct {
    value string
}

// Value は UUID の値を返す
func (u UUID) Value() string {
    return u.value
}

// ファクトリ関数:バリデーション付きで UUID を生成する
func NewUUID(value string) (UUID, error) {
    if !isValidUUID(value) {
        return UUID{}, ErrInvalidUUID
    }
    return UUID{value: value}, nil
}

// プライベートなバリデーション関数(簡易的なUUIDv4形式チェック)
func isValidUUID(value string) bool {
    return len(value) == 36 && 
           value[8] == '-' && 
           value[13] == '-' && 
           value[18] == '-' && 
           value[23] == '-'
}

UUIDGenerator のメソッドとファクトリ関数が同名であるが、それぞれのスコープが異なるため問題はない。

テストコードの修正

テストコードでは、ファクトリ関数を通じて UUID を生成するように変更する。

// モックUUIDGenerator実装
type mockUUIDGenerator struct{}

func (mockUUIDGenerator) NewUUID() (domain.UUID, error) {
    return domain.NewUUID("550e8400-e29b-41d4-a716-446655440000")
}

まとめ

ドメイン層の構造体にカプセル化された非公開フィールドがある場合、外部から直接生成することができない。
そのような状況でも、ファクトリ関数を導入することで、生成ロジックの一貫性を保ちつつ、外部からのインスタンス生成が可能になる。

特にテストやモックの場面で、値オブジェクトの生成に制限がある場合には、ファクトリ関数が非常に有効な手段となる。

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?