# 重構 --- 不改變代碼的外部行為情況下而修改原始碼 不修正錯誤,又不增加新的功能性 為了讓將來以後更好維護 --- Uncle Ben: 「With great power comes great responsibility」 --- "fat model, skinny controller" --- Obese Models --- * Value Objects * Service Objects * Form Objects * Query Objects --- What is a Value Object? --- As stated by Martin Fowler a value object is: > A small simple object, like money or a date range, whose equality isn’t based on identity. --- ### How can we identify them? 1. Arguments together all the time 2. One attribute with behaviour 3. Two inseparable attributes value and unit 4. Class enumerable --- Arguments together all the time: ```ruby= class DateRange attr_reader :start_date, :end_date def initialize(start_date, end_date) @start_date, @end_date = start_date, end_date end def include_date?(date) date >= start_date && date <= end_date end def include_date_range?(date_range) start_date <= date_range.start_date && end_date >= date_range.end_date end def to_s "from #{start_date.strftime('%d-%B-%Y')} to #{end_date.strftime('%d-%B-%Y')}" end end ``` --- ```ruby= class Event < ActiveRecord::Base def date_range DateRange.new(start_date, end_date) end def date_range=(date_range) self.start_date = date_range.start_date self.end_date = date_range.end_date end end ``` --- ```ruby= > event = Event.create(name: 'Ruby conf', start_date: Date.today, end_date: Date.today + 1.days) > event.date_range.include_date?(Date.today) => true ``` --- One attribute with behaviour: ```ruby= room = Room.create(degrees: 10) room.cold? room.hot? ``` --- ```ruby= class Temperature attr_reader :degrees COLD = 20 HOT = 25 def initialize(degrees) @degrees = degrees end def cold? self < COLD end def hot? self > HOT end def to_s "#{degrees} °C" end end ``` --- ```ruby= > room = Room.create(degrees: 10) > room.temperature.cold? => true > room.temperature.hot? => false ``` --- ### Two inseparable attributes value and unit temperature (degrees and unit) money (cents and currency) distance (value and unit) --- https://github.com/RubyMoney/money --- ![](https://i.imgur.com/Wq64ct4.png) --- Class enumerable ```ruby= class Event < ActiveRecord::Base SIZE = %w( small medium big ) end ``` --- ```ruby= class Size SIZES = %w(small medium big) attr_reader :size def initialize(size) @size = size end def self.to_select SIZES.map{|c| [c.capitalize, c]} end def valid? SIZES.include?(size) end def to_s size.capitalize end end ``` --- 透過寫一些簡單的 Ruby 類別,把一些判斷的邏輯放在裡面。這樣的純 Ruby 類別,又稱之 PORO(Plain Old Ruby Object) --- 把跟該 Model 看起來不相關功能的程式碼都拆出來,除了可增加彈性且更容易重複使用外,將來也容易維護修改,也因為程式碼另外拆出來而變得容易測試 --- [Value Objects in Ruby on Rails](https://revs.runtime-revolution.com/value-objects-in-ruby-on-rails-9df64bc8db34) [Top Tips for Refactoring Fat Models in Rails](https://airbrake.io/blog/rails/top-tips-for-refactoring-rails-models) [5 Common Mistakes in Rails Development](https://airbrake.io/blog/rails/5-common-mistakes-rails-development)
{"metaMigratedAt":"2023-06-15T09:58:17.361Z","metaMigratedFrom":"Content","title":"重構","breaks":true,"contributors":"[{\"id\":\"a1061309-7a7f-42b9-b144-e7e479bb7747\",\"add\":3336,\"del\":229}]"}
    154 views