アクセサ

2 件の記事

Contents

Don’t use getters unnecessarily. (不必要にゲッターを使わない)

解説

すべてのフィールドに機械的にゲッターを用意することは、カプセル化の本質を見失った形式的な実装です。ゲッターは、外部に公開する必要がある情報に対してのみ提供すべきです。不必要なゲッターは、内部実装の詳細を露出させ、クラスの責任を不明確にします。本当に必要な場合のみゲッターを作成することで、適切なカプセル化が実現され、変更の影響範囲が限定されます。

具体例

// 悪い例(すべてのフィールドにゲッターを用意)
type Order struct {
    id          string
    customerID  string
    items       []Item
    subtotal    float64
    tax         float64
    shipping    float64
    createdAt   time.Time
    updatedAt   time.Time
}

// すべてのフィールドに機械的にゲッターを用意
func (o *Order) ID() string           { return o.id }
func (o *Order) CustomerID() string   { return o.customerID }
func (o *Order) Items() []Item        { return o.items }
func (o *Order) Subtotal() float64    { return o.subtotal }
func (o *Order) Tax() float64         { return o.tax }
func (o *Order) Shipping() float64    { return o.shipping }
func (o *Order) CreatedAt() time.Time { return o.createdAt }
func (o *Order) UpdatedAt() time.Time { return o.updatedAt }

// 良い例(本当に必要なゲッターのみ提供)
type Order struct {
    id         string
    customerID string
    items      []Item
    subtotal   float64
    tax        float64
    shipping   float64
    createdAt  time.Time
    updatedAt  time.Time
}

// 外部に公開が必要な情報のみゲッターを提供
func (o *Order) ID() string {
    return o.id
}

func (o *Order) CustomerID() string {
    return o.customerID
}

// 計算されたtotalのみ公開(内訳は公開しない)
func (o *Order) Total() float64 {
    return o.subtotal + o.tax + o.shipping
}

// ItemsのゲッターではなくItemの数のみ公開
func (o *Order) ItemCount() int {
    return len(o.items)
}

// さらに良い例(ドメインメソッドとして提供)
func (o *Order) IsExpired() bool {
    return time.Since(o.createdAt) > 30*24*time.Hour
}

func (o *Order) CanBeCancelled() bool {
    return time.Since(o.createdAt) < 24*time.Hour
}

// 内部計算フィールドは完全に隠蔽
func (o *Order) calculateSubtotal() float64 {
    total := 0.0
    for _, item := range o.items {
        total += item.Price * float64(item.Quantity)
    }
    return total
}

func (o *Order) calculateTax() float64 {
    return o.subtotal * 0.1
}

参考リンク

Contents

Access struct fields through getter methods; avoid setters. (構造体のフィールドへのアクセスはゲッターメソッドを通して行い、セッターは避ける)

解説

セッターメソッドを提供すると、オブジェクトの状態がいつでも変更可能になり、不変性が損なわれます。ゲッターのみを提供することで、オブジェクトの読み取り専用アクセスを実現し、予期しない状態変更を防げます。状態の変更が必要な場合は、ビジネスロジックを含むドメインメソッドとして実装すべきです。これにより、状態変更に対する制御が強化され、バグの混入を防ぎます。

具体例

// 悪い例(セッターが存在)
type Account struct {
    balance float64
}

func (a *Account) GetBalance() float64 {
    return a.balance
}

func (a *Account) SetBalance(amount float64) {
    a.balance = amount  // 検証なしで変更可能
}

func main() {
    acc := &Account{balance: 1000}
    acc.SetBalance(-500)  // 不正な状態を作成可能
}

// 良い例(ゲッターのみ、状態変更はドメインメソッド)
type Account struct {
    balance float64
}

func NewAccount(initialBalance float64) (*Account, error) {
    if initialBalance < 0 {
        return nil, errors.New("initial balance must be positive")
    }
    return &Account{balance: initialBalance}, nil
}

func (a *Account) Balance() float64 {
    return a.balance
}

func (a *Account) Deposit(amount float64) error {
    if amount <= 0 {
        return errors.New("deposit amount must be positive")
    }
    a.balance += amount
    return nil
}

func (a *Account) Withdraw(amount float64) error {
    if amount <= 0 {
        return errors.New("withdraw amount must be positive")
    }
    if a.balance < amount {
        return errors.New("insufficient balance")
    }
    a.balance -= amount
    return nil
}

参考リンク