リファクタリングしてみた(ハギスの移動距離)

適用したリファクタリング

  • 一時変数から問い合わせメソッドへ(Replace Temp with Query)
  • 条件文の分解(Decompose Conditional)

プロダクトコード(ruby

before

class Haggis
  def initialize(primary_force, secondary_force, mass, delay)
    @primary_force, @secondary_force, @mass, @delay = primary_force, secondary_force, mass, delay
  end

  def distance_traveled(time)
    primary_acc = @primary_force / @mass
    primary_time = [time, @delay].min
    result = 0.5 * primary_acc * primary_time * primary_time
    secondary_time = time - @delay
    if (secondary_time > 0)
      primary_vel = primary_acc * @delay
      secondary_acc = (@primary_force + @secondary_force) / @mass
      result += primary_vel * secondary_time + 5 * secondary_acc * secondary_time * secondary_time
    end
    result
  end
end

after

class Haggis
  def initialize(primary_force, secondary_force, mass, delay)
    @primary_force, @secondary_force, @mass, @delay = primary_force, secondary_force, mass, delay
  end

  def distance_traveled(time)
    result = primary_distance_traveled(time)
    result += secondary_distance_traveled(time) if accelerate_secondary?(time)
    result
  end

  def primary_distance_traveled(time)
    0.5 * primary_acc * primary_time(time) * primary_time(time)
  end

  def secondary_distance_traveled(time)
    primary_vel * secondary_time(time) + 5 * secondary_acc * secondary_time(time) * secondary_time(time)
  end

  def primary_acc
    @primary_force / @mass
  end

  def secondary_acc
    (@primary_force + @secondary_force) / @mass
  end

  def primary_time(time)
    [time, @delay].min
  end

  def secondary_time(time)
    time - @delay
  end

  def primary_vel
    primary_acc * @delay
  end

  def accelerate_secondary?(time)
    secondary_time(time) > 0
  end
end

テストコード(rspec

before

describe Haggis do
  let(:primary_force){ 10 }
  let(:secondary_force){ 20 }
  let(:mass){ 2 }
  let(:delay){ 5 }
  let(:haggis){ Haggis.new(primary_force, secondary_force, mass, delay) }

  describe 'distance_traveled(time)' do
    subject{ haggis.distance_traveled(time) }
    context 'secondary_time <= 0' do
      let(:time){ 5 }
      it{ should == 62.5 }
    end
    context 'secondary_time > 0' do
      let(:time){ 6 }
      it{ should == 162.5 }
    end
  end
end

after

describe Haggis do
  let(:primary_force){ 10 }
  let(:secondary_force){ 20 }
  let(:mass){ 2 }
  let(:delay){ 5 }
  let(:time){ 10 }
  let(:haggis){ Haggis.new(primary_force, secondary_force, mass, delay) }

  describe 'distance_traveled(time)' do
    let(:primary_distance_traveled){ 62.5 }
    let(:secondary_distance_traveled){ 2000 }
    before do
      haggis.should_receive(:accelerate_secondary?).with(time).and_return(accelerate_secondary_key)
      haggis.should_receive(:primary_distance_traveled).with(time).and_return(primary_distance_traveled)
    end
    subject{ haggis.distance_traveled(time) }

    context 'not accelerate secondary' do
      let(:accelerate_secondary_key){ false }
      before do
        haggis.should_not_receive(:secondary_distance_traveled)
      end
      it{ should == 62.5 }
    end
    context 'accelerate secondary' do
      let(:accelerate_secondary_key){ true }
      before do
        haggis.should_receive(:secondary_distance_traveled).with(time).and_return(secondary_distance_traveled)
      end
      it{ should == 2062.5 }
    end
  end

  describe 'primary_distance_traveled(time)' do
    subject{ haggis.primary_distance_traveled(time) }
    it{ should == 62.5 }
  end

  describe 'secondary_distance_traveled(time)' do
    subject{ haggis.secondary_distance_traveled(time) }
    it{ should == 2000 }
  end

  describe 'accelerate_secondary?(time)' do
    before do
      haggis.should_receive(:secondary_time).with(time).and_return(secondary_time)
    end
    subject{ haggis.accelerate_secondary?(time) }

    context 'secondary_time > 0' do
      let(:secondary_time){ 1 }
      it{ should be_true }
    end
    context 'secondary_time <= 0' do
      let(:secondary_time){ 0 }
      it{ should be_false }
    end
  end
end

※ 参考資料
 リファクタリング:Rubyエディション6.7 一時変数の分割(Split Temporary Variable)(p.147〜149)