💬

Rubyで保守性の高いClassの基礎的な書き方

2023/05/30に公開

最近以下の書籍を読んで、コードでの設計についてキャッチアップしているので、
備忘録として、適宜アウトプットしていきます。

良いコード/悪いコードで学ぶ設計入門―保守しやすい 成長し続けるコードの書き方
https://www.amazon.co.jp/dp/B09Y1MWK9N/

オブジェクト指向言語だとRubyが慣れているので、Rubyでアウトプットします。
書籍に記載のある例を利用するとamountとcurrencyのインスタンス変数と金額を変更できるaddメソッドを持つ、Money Classを作成してくださいと言われた場合、以下のようなコードを書いてしまいがちですが、保守性が低くバグの温床になる可能性があります。

class Money
  attr_accessor :amount, :currency

  def initialize(amount, currency)
    @amount = amount
    @currency = currency
  end

  def add(amount, currency)
    raise "Cannot add different currencies." if @currency != currency

    @amount += amount
  end
end

上記の場合だと初期化時に以下のように意図しない値が入り込んでしまう可能性があるし、
amountで保持している金額が可変のため、値を直接変更できてしまい、今いくらかどうかをわからなくなってしまうデメリットがあるそうです。

#  マイナス円でも初期化できてしまう。
money = Money.new(-100, 'USD')

そこで初期化時にvalidationを追加して、initializeするようにして、
可変にしたくない金額については、不変にして新しいインスタンスを返すようにして、
値の直接代入をできなくすると、変更が限定的にできるのできるそうです。

class Money
  attr_accessor :amount, :currency

  def initialize(amount, currency)
    if amount < 0
	raise ArgumentError.new('0以上の値を指定してください')
    if currency.nil? || currency.empty?
	raise ArgumentError.new('通貨を指定してください')
    @amount = amount
    @currency = currency
    self.freeze #不変にする
  end

  def add(amount, currency)
    raise "Cannot add different currencies." if @currency != currency
    added = @amount + amount
    
    return Moeny.New(added, @currency) #不変なので新しいインスタンスを返すようにする。
  end
end

Discussion