Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Making tastier code through Refactoring

Making tastier code through Refactoring

All we have ever worked with an application with legacy code. Refactoring is a technique that allows us to restructure and redesign our application code without changing its behavior so that it is more readable and easier to maintain. This presentation discusses the advantages and disadvantages of refactoring as well as some of the main techniques that apply during a refactoring.

Gabriel Ortuño

October 25, 2012
Tweet

More Decks by Gabriel Ortuño

Other Decks in Programming

Transcript

  1. "Refactoring is the process of changing a software system in

    such a way that it does not alter the external behavior of the code yet improves its internal structure" Martin Fowler
  2. class Recipe ... def nutritional_report total_calories, nutritional_points = 0, 0

    result = "Nutritional Report for #{name}\n" self.ingredients.each do |ingredient| this_calories = 0 # add calories by ingredient case ingredient.food.nutritional_code when Food::HIGH this_calories += 5 this_calories += (ingredient.amount - 2) * 1.5 if ingredient.amount > 2 when Food::LOW this_calories += ingredient.amount * 3 when Food::REGULAR this_calories += 1.5 this_calories += (ingredient.amount - 3) * 1.5 if ingredient.amount > 3 end # add nutritional points nutritional_points += 1 # add extra nutritional points for high food if ingredient.food.nutritional_code == Food::HIGH && ingredient.amount > 1 nutritional_points += 1 end # show figures for this rental result += "\t" + ingredient.food.name + "\t" + this_calories.to_s + "\n" total_calories += this_calories end # add footer lines result += "Total calories are #{total_calories}\n" result += "You earned #{nutritional_points} nutritional points" result end end
  3. describe Recipe do let(:recipe) { Recipe.new("Lentils with chorizo") } let(:chorizo)

    { Food.new('chorizo', Food::HIGH) } let(:lentil) { Food.new('lentil', Food::LOW) } let(:potatoe) { Food.new('potatoe', Food::REGULAR) } it "has a name" do recipe.name.should == "Lentils with chorizo" end describe "calories" do it "without ingredients are 0" it "with one regular ingredient are 1.5" it "with one regular ingredient and amount > 3 are 3" it "with one high ingredient are 5" end ... end
  4. class Recipe ... def nutritional_report total_calories, nutritional_points = 0, 0

    result = "Nutritional Report for #{name}\n" self.ingredients.each do |ingredient| this_calories = 0 # add calories by ingredient case ingredient.food.nutritional_code when Food::HIGH this_calories += 5 this_calories += (ingredient.amount - 2) * 1.5 if ingredient.amount > 2 when Food::LOW this_calories += ingredient.amount * 3 when Food::REGULAR this_calories += 1.5 this_calories += (ingredient.amount - 3) * 1.5 if ingredient.amount > 3 end # add nutritional points nutritional_points += 1 # add extra nutritional points for high food if ingredient.food.nutritional_code == Food::HIGH && ingredient.amount > 1 nutritional_points += 1 end # show figures for this rental result += "\t" + ingredient.food.name + "\t" + this_calories.to_s + "\n" total_calories += this_calories end # add footer lines result += "Total calories are #{total_calories}\n" result += "You earned #{nutritional_points} nutritional points" result end end
  5. class Recipe ... def nutritional_report total_calories, nutritional_points = 0, 0

    result = "Nutritional Report for #{name}\n" self.ingredients.each do |ingredient| this_calories = 0 # add calories by ingredient case ingredient.food.nutritional_code when Food::HIGH this_calories += 5 this_calories += (ingredient.amount - 2) * 1.5 if ingredient.amount > 2 when Food::LOW this_calories += ingredient.amount * 3 when Food::REGULAR this_calories += 1.5 this_calories += (ingredient.amount - 3) * 1.5 if ingredient.amount > 3 end # add nutritional points nutritional_points += 1 # add extra nutritional points for high food if ingredient.food.nutritional_code == Food::HIGH && ingredient.amount > 1 nutritional_points += 1 end # show figures for this rental result += "\t" + ingredient.food.name + "\t" + this_calories.to_s + "\n" total_calories += this_calories end # add footer lines result += "Total calories are #{total_calories}\n" result += "You earned #{nutritional_points} nutritional points" result end end
  6. ... # add calories by ingredient case ingredient.food.nutritional_code when Food::HIGH

    this_calories += 5 this_calories += (ingredient.amount - 2) * 1.5 if ... when Food::LOW this_calories += ingredient.amount * 3 when Food::REGULAR this_calories += 1.5 this_calories += (ingredient.amount - 3) * 1.5 if ... end ...
  7. class Recipe ... def calories_for(ingredient) case ingredient.food.nutritional_code when Food::HIGH this_calories

    += (ingredient.amount - 2) * 1.5 if ... this_calories += 5 when Food::LOW this_calories += ingredient.amount * 3 when Food::REGULAR this_calories += (ingredient.amount - 3) * 1.5 if ... this_calories += 1.5 end end end
  8. def nutritional_report total_calories, nutritional_points = 0, 0 result = "Nutritional

    Report for #{name}\n" self.ingredients.each do |ingredient| this_calories = calories_for(ingredient) # add nutritional points nutritional_points += 1 # add extra nutritional points for high food if ingredient.food.nutritional_code == Food::HIGH && ingredient. amount > 1 nutritional_points += 1 end # show figures for this rental result += "\t" + ingredient.food.name + "\t" + this_calories.to_s + "\n" total_calories += this_calories end # add footer lines result += "Total calories are #{total_calories}\n" result += "You earned #{nutritional_points} nutritional points" result end
  9. class Recipe ... def calories_for(ingredient) case ingredient.food.nutritional_code when Food::HIGH this_calories

    += (ingredient.amount - 2) * 1.5 if ... this_calories += 5 when Food::LOW this_calories += ingredient.amount * 3 when Food::REGULAR this_calories += (ingredient.amount - 3) * 1.5 if ... this_calories += 1.5 end end end
  10. class Recipe ... def calories_for(ingredient) this_calories = 0 case ingredient.food.nutritional_code

    when Food::HIGH this_calories += (ingredient.amount - 2) * 1.5 if ... this_calories += 5 when Food::LOW this_calories += ingredient.amount * 3 when Food::REGULAR this_calories += (ingredient.amount - 3) * 1.5 if ... this_calories += 1.5 end end end
  11. class Recipe ... def calories_for(ingredient) this_calories = 0 case ingredient.food.nutritional_code

    when Food::HIGH this_calories += (ingredient.amount - 2) * 1.5 if ... this_calories += 5 when Food::LOW this_calories += ingredient.amount * 3 when Food::REGULAR this_calories += (ingredient.amount - 3) * 1.5 if ... this_calories += 1.5 end end end
  12. class Recipe ... def calories_for(ingredient) this_calories = 0 case ingredient.food.nutritional_code

    when Food::HIGH this_calories += (ingredient.amount - 2) * 1.5 if ... this_calories += 5 when Food::LOW this_calories += ingredient.amount * 3 when Food::REGULAR this_calories += (ingredient.amount - 3) * 1.5 if ... this_calories += 1.5 end end end
  13. class Ingredient ... def calories this_calories = 0 case food.nutritional_code

    when Food::HIGH this_calories += 5 this_calories += (amount - 2) * 1.5 if amount > 2 when Food::LOW this_calories += amount * 3 when Food::REGULAR this_calories += 1.5 this_calories += (amount - 3) * 1.5 if amount > 3 end end end
  14. class Recipe def nutritional_report total_calories, nutritional_points = 0, 0 result

    = "Nutritional Report for #{name}\n" self.ingredients.each do |ingredient| # add nutritional points nutritional_points += 1 # add extra nutritional points for high food if ingredient.food.nutritional_code == Food::HIGH && ingredient. amount > 1 nutritional_points += 1 end # show figures for this rental result += "\t" + ingredient.food.name + "\t" result += ingredient.calories.to_s + "\n" total_calories += ingredient.calories end # add footer lines result += "Total calories are #{total_calories}\n" result += "You earned #{nutritional_points} nutritional points" result end end
  15. describe Ingredient do let(:chorizo) { Food.new('chorizo',Food::HIGH) } let(:lentil) { Food.new('lentil',

    Food::LOW) } let(:potatoe) { Food.new('potatoe', Food::REGULAR) } describe 'calories' do it "with one regular food are 1.5" it "with one regular food and amount > 3 are 3" it "with one high food are 5" it "with one high food and amount > 2 are 6.5" it "with one low food are 3" end end
  16. class Recipe def nutritional_report total_calories, nutritional_points = 0, 0 result

    = "Nutritional Report for #{name}\n" self.ingredients.each do |ingredient| # add nutritional points nutritional_points += 1 # add extra nutritional points for high food if ingredient.food.nutritional_code == Food::HIGH && ingredient.amount > 1 nutritional_points += 1 end # show figures for this rental result += "\t" + ingredient.food.name + "\t" result += ingredient.calories.to_s + "\n" total_calories += ingredient.calories end # add footer lines result += "Total calories are #{total_calories}\n" result += "You earned #{nutritional_points} nutritional points" result end end
  17. class Recipe ... def nutritional_report total_calories, nutritional_points = 0, 0

    result = "Nutritional Report for #{name}\n" self.ingredients.each do |ingredient| nutritional_points += ingredient.nutritional_points # show figures for this rental result += "\t" + ingredient.food.name + "\t" result += ingredient.calories.to_s + "\n" total_calories += ingredient.calories end # add footer lines result += "Total calories are #{total_calories}\n" result += "You earned #{nutritional_points} nutritional point result end end
  18. describe Ingredient do describe "nutritional points" do it "is 2

    if food is high and amount > 1" it "is 1 if food is high and amount = 1" it "is 1 if food is not high and amount = 1" it "is 1 if food is not high and amount > 1" end end
  19. class Recipe def nutritional_report total_calories, nutritional_points = 0, 0 result

    = "Nutritional Report for #{name}\n" self.ingredients.each do |ingredient| nutritional_points += ingredient.nutritional_points # show figures for this rental result += "\t" + ingredient.food.name + "\t" result += ingredient.calories.to_s + "\n" total_calories += ingredient.calories end # add footer lines result += "Total calories are #{total_calories}\n" result += "You earned #{nutritional_points} nutritional point result end end
  20. class Recipe def nutritional_report result = "Nutritional Report for #{name}\n"

    ingredients.each do |ingredient| # show figures for this rental result += "\t" + ingredient.food.name + "\t" result += ingredient.calories.to_s + "\n" end # add footer lines result += "Total calories are #{total_calories}\n" result += "You earned #{total_nutritional_points} nutritional result end ... end
  21. class Recipe def nutritional_report result = "Nutritional Report for #{name}\n"

    ingredients.each do |ingredient| # show figures for this rental result += "\t" + ingredient.food.name + "\t" result += ingredient.calories.to_s + "\n" end # add footer lines result += "Total calories are #{total_calories}\n" result += "You earned #{total_nutritional_points} nutritional result end ... end
  22. class Recipe def html_nutritional_report result = "<h1>Nutritional Report for #{name}</h1>"

    ingredients.each do |ingredient| # show figures for this rental result += "<p>#{ingredient.food.name} " result += "{ingredient.calories}</p>" end # add footer lines result += "<p>Total calories are #{total_calories}</p>" result += "<p>You earned #{total_nutritional_points} " result += "nutritional points</p>" result end ... end HTML Report
  23. class NutritionalReport def initialize(recipe) @recipe = recipe end def output

    head body foot end def head ... def body ... def line(ingredient) ... def foot ... end
  24. class HTMLNutritionalReport < NutritionalReport def head "<h1>Nutritional Report for #{name}</h1>"

    end def line(ingredient) "<p>#{ingredient.food.name} #{ingredient.calories}</p>" end def foot result = "<p>Total calories are #{@recipe.total_calories}</p result += "<p>You earned #{@recipe.total_nutritional_points} nutritional points</p>" result end end
  25. class Ingredient ... def calories this_calories = 0 case food.nutritional_code

    when Food::HIGH this_calories += (amount - 2) * 1.5 if amount > 2 this_calories += 5 when Food::LOW this_calories += amount * 3 when Food::REGULAR this_calories += (amount - 3) * 1.5 if amount > 3 this_calories += 1.5 end end def nutritional_points (food.nutritional_code == Food::HIGH && amount > 1) ? 2 : 1 end ... end
  26. class Food def calories(amount) this_calories = 0 case nutritional_code when

    HIGH this_calories += (amount - 2) * 1.5 if amount > 2 this_calories += 5 when LOW this_calories += amount * 3 when REGULAR this_calories += (amount - 3) * 1.5 if amount > 3 this_calories += 1.5 end end def nutritional_points(amount) (nutritional_code == HIGH && amount > 1) ? 2 : 1 end end
  27. describe Ingredient do let(:chorizo) { Food.new('chorizo', Food::HIGH) } let(:lentil) {

    Food.new('lentil', Food::LOW) } let(:potatoe) { Food.new('potatoe', Food::REGULAR) } describe 'calories' do it "with one regular food are 1.5" it "with one regular food and amount > 3 are 3" it "with one high food are 5" it "with one high food and amount > 2 are 6.5" it "with one low food are 3" end describe "nutritional points" do it "is 2 if food is high and amount > 1" it "is 1 if food is high and amount = 1" it "is 1 if food is not high and amount = 1" it "is 1 if food is not high and amount > 1" end end
  28. class Food def calories(amount) this_calories = 0 case nutritional_code when

    HIGH this_calories += (amount - 2) * 1.5 if amount > 2 this_calories += 5 when LOW this_calories += amount * 3 when REGULAR this_calories += (amount - 3) * 1.5 if amount > 3 this_calories += 1.5 end end def nutritional_points(amount) (nutritional_code == HIGH && amount > 1) ? 2 : 1 end end
  29. class Food ... def nutritional_code=(value) @nutritional_code = value @nutritional_type =

    case @nutritional_code when HIGH then HighNutritional.new when LOW then LowNutritional.new when REGULAR then RegularNutritional.new end end def calories(amount) @nutritional_type.calories(amount) end def nutritional_points(amount) @nutritional_type.points(amount) end end
  30. module DefaultNutritionalPoints def points(amount) 1 end end class RegularNutritional include

    DefaultNutritionalPoints def calories(amount) acum = 1.5 acum += (amount - 3) * 1.5 if amount > 3 acum end end
  31. class LowNutritional include DefaultNutritionalPoints def calories(amount) amount * 3 end

    end class HighNutritional def calories(amount) acum = 5 acum += (amount - 2) * 1.5 if amount > 2 acum end def points(amount) amount > 1 ? 2 : 1 end end
  32. References • Refactoring: Improving design of existing code - Martin

    Fowler • Refactoring to Patterns - Joshua Kerievsky • Clean Code - Robert C. Martin • Design Patterns in Ruby - Russ Olsen • Source Making http://sourcemaking.com/refactoring
  33. Tools • Reek - Code Smell Detector for ruby https://github.com/troessner/reek

    • Rails Best Practices http://rails-bestpractices.com • Code Climate http://codeclimate.com • Ruby Refactoring Tool for Vim https://github.com/ecomba/vim-ruby-refactoring