SlideShare a Scribd company logo
1 of 85
Download to read offline
Making tastier code through

Refactoring
Person.new(
    name: 'Gabriel Ortuño',
    job: 'ASPgems',
    web: 'arctarus.com',
    pet_project: 'rezets.com',
    github: 'arctarus',
    twitter: 'arctarus'
)
1. Introduction

  2. Sample

3. Conclusions
Refactoring?
"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
Code Smells
Refactoring Toolbox
Why?
Green Field
Legacy Code
When?
1. Introduction

  2. Sample

3. Conclusions
New Task
Print nutritional report in HTML
Recipe
                             Ingredient
name                 1:N
ingredients                 amount
                            food
nutritional_report

                                     1:1

                                 Food
                           HIGH
                           LOW
                           REGULAR
                           name
                           nutritional_code
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
Whyyyy?
1º Build a solid set of tests
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
$ rspec spec
..............

Finished in 0.00742 seconds
14 examples, 0 failures
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
Long Method
Comments
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
...
# 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
...
Extract Method
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
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
$ rspec spec
.FFFFFFFFFFFFF

Finished in 0.00742 seconds
14 examples, 13 failures
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
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
$ rspec spec
..............

Finished in 0.00742 seconds
14 examples, 0 failures
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
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
Feature Envy
Move Method
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
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
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
$ rspec spec
...................

Finished in 0.00588 seconds
19 examples, 0 failures
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
Remove

 Feature Envy

     with

Extract Method
class Ingredient
  ...
  def nutritional_points
    if food.nutritional_code == Food::HIGH && amount > 1
      2
    else
      1
    end
  end
end
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
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
$ rspec spec
.......................

Finished in 0.00588 seconds
23 examples, 0 failures
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
Replace Temp with Query
class Recipe
  ...

  def total_calories
    ingredients.sum(:calories)
  end

  def total_nutritional_points
    ingredients.sum(:nutritional_points)
  end
end
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
$ rspec spec
.......................

Finished in 0.00621 seconds
23 examples, 0 failures
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
HTML Report
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
More Refactoring?



   Replace Method
  with Method Objet

Template Method Pattern
class NutritionalReport

  def initialize(recipe)
    @recipe = recipe
  end

  def output
    head
    body
    foot
  end

  def   head
  ...
  def   body
  ...
  def   line(ingredient)
  ...
  def   foot
  ...
end
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
WIN!
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
I notice a weird smell...




  Could it be envy?
Get rid of
Feature Envy
   with
Move Method
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
class Ingredient
  def calories
    food.calories(amount)
  end

  def nutritional_points
    food.nutritional_points(amount)
  end
end
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
$ rspec spec/
................................

Finished in 0.00865 seconds
32 examples, 0 failures
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
Fix
  Switch Statements


Replace Type Code with
    State/Strategy
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
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
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
EPIC
WIN!
1. Introduction

  2. Sample

3. Conclusions
No Silver Bullet
Improve Design
Helps find bugs
Program faster
Good programmers write
 code that humans can
      understand
Refactor
 to Win
¡Thanks!
Questions?
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
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
Thanks!

More Related Content

Recently uploaded

FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhi
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | DelhiFULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhi
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhisoniya singh
 
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...gurkirankumar98700
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationRadu Cotescu
 
Slack Application Development 101 Slides
Slack Application Development 101 SlidesSlack Application Development 101 Slides
Slack Application Development 101 Slidespraypatel2
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdfhans926745
 
IAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsIAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsEnterprise Knowledge
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptxHampshireHUG
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Drew Madelung
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerThousandEyes
 
Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slidevu2urc
 
Google AI Hackathon: LLM based Evaluator for RAG
Google AI Hackathon: LLM based Evaluator for RAGGoogle AI Hackathon: LLM based Evaluator for RAG
Google AI Hackathon: LLM based Evaluator for RAGSujit Pal
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationMichael W. Hawkins
 
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking MenDelhi Call girls
 
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure serviceWhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure servicePooja Nehwal
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Miguel Araújo
 
🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘RTylerCroy
 
Understanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitectureUnderstanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitecturePixlogix Infotech
 
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfThe Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfEnterprise Knowledge
 
Breaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountBreaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountPuma Security, LLC
 
Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...Alan Dix
 

Recently uploaded (20)

FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhi
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | DelhiFULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhi
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhi
 
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organization
 
Slack Application Development 101 Slides
Slack Application Development 101 SlidesSlack Application Development 101 Slides
Slack Application Development 101 Slides
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf
 
IAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsIAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI Solutions
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slide
 
Google AI Hackathon: LLM based Evaluator for RAG
Google AI Hackathon: LLM based Evaluator for RAGGoogle AI Hackathon: LLM based Evaluator for RAG
Google AI Hackathon: LLM based Evaluator for RAG
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day Presentation
 
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
 
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure serviceWhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
 
🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘
 
Understanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitectureUnderstanding the Laravel MVC Architecture
Understanding the Laravel MVC Architecture
 
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfThe Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
 
Breaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountBreaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path Mount
 
Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...
 

Featured

2024 State of Marketing Report – by Hubspot
2024 State of Marketing Report – by Hubspot2024 State of Marketing Report – by Hubspot
2024 State of Marketing Report – by HubspotMarius Sescu
 
Everything You Need To Know About ChatGPT
Everything You Need To Know About ChatGPTEverything You Need To Know About ChatGPT
Everything You Need To Know About ChatGPTExpeed Software
 
Product Design Trends in 2024 | Teenage Engineerings
Product Design Trends in 2024 | Teenage EngineeringsProduct Design Trends in 2024 | Teenage Engineerings
Product Design Trends in 2024 | Teenage EngineeringsPixeldarts
 
How Race, Age and Gender Shape Attitudes Towards Mental Health
How Race, Age and Gender Shape Attitudes Towards Mental HealthHow Race, Age and Gender Shape Attitudes Towards Mental Health
How Race, Age and Gender Shape Attitudes Towards Mental HealthThinkNow
 
AI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdfAI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdfmarketingartwork
 
PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024Neil Kimberley
 
Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)contently
 
How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024Albert Qian
 
Social Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie InsightsSocial Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie InsightsKurio // The Social Media Age(ncy)
 
Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024Search Engine Journal
 
5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summarySpeakerHub
 
ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd Clark Boyd
 
Getting into the tech field. what next
Getting into the tech field. what next Getting into the tech field. what next
Getting into the tech field. what next Tessa Mero
 
Google's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search IntentGoogle's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search IntentLily Ray
 
Time Management & Productivity - Best Practices
Time Management & Productivity -  Best PracticesTime Management & Productivity -  Best Practices
Time Management & Productivity - Best PracticesVit Horky
 
The six step guide to practical project management
The six step guide to practical project managementThe six step guide to practical project management
The six step guide to practical project managementMindGenius
 
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...RachelPearson36
 

Featured (20)

2024 State of Marketing Report – by Hubspot
2024 State of Marketing Report – by Hubspot2024 State of Marketing Report – by Hubspot
2024 State of Marketing Report – by Hubspot
 
Everything You Need To Know About ChatGPT
Everything You Need To Know About ChatGPTEverything You Need To Know About ChatGPT
Everything You Need To Know About ChatGPT
 
Product Design Trends in 2024 | Teenage Engineerings
Product Design Trends in 2024 | Teenage EngineeringsProduct Design Trends in 2024 | Teenage Engineerings
Product Design Trends in 2024 | Teenage Engineerings
 
How Race, Age and Gender Shape Attitudes Towards Mental Health
How Race, Age and Gender Shape Attitudes Towards Mental HealthHow Race, Age and Gender Shape Attitudes Towards Mental Health
How Race, Age and Gender Shape Attitudes Towards Mental Health
 
AI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdfAI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdf
 
Skeleton Culture Code
Skeleton Culture CodeSkeleton Culture Code
Skeleton Culture Code
 
PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024
 
Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)
 
How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024
 
Social Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie InsightsSocial Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie Insights
 
Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024
 
5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary
 
ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd
 
Getting into the tech field. what next
Getting into the tech field. what next Getting into the tech field. what next
Getting into the tech field. what next
 
Google's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search IntentGoogle's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search Intent
 
How to have difficult conversations
How to have difficult conversations How to have difficult conversations
How to have difficult conversations
 
Introduction to Data Science
Introduction to Data ScienceIntroduction to Data Science
Introduction to Data Science
 
Time Management & Productivity - Best Practices
Time Management & Productivity -  Best PracticesTime Management & Productivity -  Best Practices
Time Management & Productivity - Best Practices
 
The six step guide to practical project management
The six step guide to practical project managementThe six step guide to practical project management
The six step guide to practical project management
 
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
 

Making tastier code through refactoring

  • 1. Making tastier code through Refactoring
  • 2. Person.new( name: 'Gabriel Ortuño', job: 'ASPgems', web: 'arctarus.com', pet_project: 'rezets.com', github: 'arctarus', twitter: 'arctarus' )
  • 3. 1. Introduction 2. Sample 3. Conclusions
  • 5. "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
  • 6.
  • 12. When?
  • 13.
  • 14. 1. Introduction 2. Sample 3. Conclusions
  • 15. New Task Print nutritional report in HTML
  • 16. Recipe Ingredient name 1:N ingredients amount food nutritional_report 1:1 Food HIGH LOW REGULAR name nutritional_code
  • 17.
  • 18. 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
  • 20. 1º Build a solid set of tests
  • 21. 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
  • 22. $ rspec spec .............. Finished in 0.00742 seconds 14 examples, 0 failures
  • 23. 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
  • 26. 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
  • 27. ... # 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 ...
  • 29. 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
  • 30. 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
  • 31. $ rspec spec .FFFFFFFFFFFFF Finished in 0.00742 seconds 14 examples, 13 failures
  • 32.
  • 33. 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
  • 34. 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
  • 35. $ rspec spec .............. Finished in 0.00742 seconds 14 examples, 0 failures
  • 36. 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
  • 37. 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
  • 40. 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
  • 41. 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
  • 42. 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
  • 43. $ rspec spec ................... Finished in 0.00588 seconds 19 examples, 0 failures
  • 44. 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
  • 45. Remove Feature Envy with Extract Method
  • 46. class Ingredient ... def nutritional_points if food.nutritional_code == Food::HIGH && amount > 1 2 else 1 end end end
  • 47. 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
  • 48. 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
  • 49. $ rspec spec ....................... Finished in 0.00588 seconds 23 examples, 0 failures
  • 50. 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
  • 52. class Recipe ... def total_calories ingredients.sum(:calories) end def total_nutritional_points ingredients.sum(:nutritional_points) end end
  • 53. 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
  • 54. $ rspec spec ....................... Finished in 0.00621 seconds 23 examples, 0 failures
  • 55. 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
  • 56. HTML Report 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
  • 57. More Refactoring? Replace Method with Method Objet Template Method Pattern
  • 58. class NutritionalReport def initialize(recipe) @recipe = recipe end def output head body foot end def head ... def body ... def line(ingredient) ... def foot ... end
  • 59. 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
  • 60. WIN!
  • 61. 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
  • 62. I notice a weird smell... Could it be envy?
  • 63. Get rid of Feature Envy with Move Method
  • 64. 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
  • 65. class Ingredient def calories food.calories(amount) end def nutritional_points food.nutritional_points(amount) end end
  • 66. 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
  • 67. $ rspec spec/ ................................ Finished in 0.00865 seconds 32 examples, 0 failures
  • 68. 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
  • 69. Fix Switch Statements Replace Type Code with State/Strategy
  • 70. 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
  • 71. 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
  • 72. 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
  • 74. 1. Introduction 2. Sample 3. Conclusions
  • 79. Good programmers write code that humans can understand
  • 83. 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
  • 84. 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