🍳

[Ruby] Factory methodで凝縮性の高いclassを作成する

2023/06/03に公開

ECサービスなどで新規入会の無料ポイントやプレミアム会員向けの特別ポイントなどを
管理するGiftPointのclassを作成する場合、以下のように作成もできます。

前に作成した記事[Rubyで保守性の高いClassの基礎的な書き方]
https://zenn.dev/articles/b181b2dcf6339b/

参照元は以下の書籍になります。
コードレベルでの設計についてキャッチアップしているので、
備忘録として、適宜アウトプットしていきます。

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

class GiftPoint
  MIN_POINT = 0
  attr_accessor :point

  def initialize(point)
    raise "Point cannot be less than or equal to zero" if point <= MIN_POINT

    @point = point

    freeze
  end

  def add(point)
    raise "Point to be added must be greater than zero" if point <= 0

    return GiftPoint.new(@point + point)
  end
end

上記のように作成してしまった場合、クラスからインスタンを生成する際のパラメーターとしてポイントを渡す必要があるため、普通の買い物に付随するポイント付与なら許容できますが、
新規入会の無料ポイントやプレミアム会員向けの特別ポイントなど、
固定のポイント付与ロジックをインスタンスの生成時に付与すると凝縮性が下がってしまい、最悪キャンペーン終了後にコードが残って、意図した挙動にならないケースも出てくる。

#呼び出し時
NEW_MEMBER_POINT = 3000
NewMembershipPoint = GiftPoint.New(NEW_MEMBER_POINT)

PREMIUM_MEMBER_POINT = 1000
PremiumMembershipPoint = GiftPoint.New(PREMIUM_MEMBER_POINT)

上記を改善するためにaddメソッドをpraivateのfactoryメソッドに変更して、
クラス外から直接呼び出すための、クラスメソッドとしてNewMembershipPoint、PremiumMembershipPointを作成するようにする。

class GiftPoint
  MIN_POINT = 0
  NEW_MEMBER_POINT = 3000
  PREMIUM_MEMBER_POINT = 1000
  attr_accessor :point

  def self.new_membership_point
    return GiftPoint.new(NEW_MEMBER_POINT)
  end
  
  def self.premium_membership_point
    return GiftPoint.new(PREMIUM_MEMBER_POINT)
  end
  
  def add(point)
    raise "Point cannot be less than zero" if point + @point < MIN_POINT
    
    return GiftPoint.new(point + @point)
  end
  
  private

  def initialize(point)
    raise "Point cannot be less than zero" if point < MIN_POINT

    @point = point
    
    self.freeze
  end
end

上記ののようにprivateのコンストラクターとクラスメソッドで外から呼び出し可能なfactoryで、
凝縮性を上げるようにすることができます。

Discussion