(Offline) Smell Exercise

What are smells?

  • Summary of Code Smells
  • Feel free to google for code smells, you will find lots of information.

Preparation

  1. On your own, take a look at the code below. You can copy it into your own text editor
  2. Get it to run, and understand what it does. You might have to experiment a little and learn some new Ruby.
  3. Study the code. Concentrate on two code smells:
  4. The unusual way that params.each works (line 3). Try to figure out what exactly that line is doing. Yes it’s pretty clever but why did I write it that way, and what are some problems with that?
  5. The convoluted way that price is computed (line 27+). Generally when you have a nested calculation like that, and secondly when a class has some kind of “code” to distinguish types, this is a code smell and tells us that there’s room for improvement.
  6. And of course, this method is much longer than it should be.

Instructions

  1. On your own, try to come up with how to improve the code. You will be doing some “refactoring” which is a fancy way to say changing the code that is written without changing it’s functionality in any way. Take 30 minutes or less doing this.
  2. Find 1 or 2 other students in our class to work with. This can be in person or over email. Discuss each person’s views on the smells. Why are they a problem? How could they be improved? Together write the best improved version of this program using your best ideas.
  3. Write up a joint summary (PDF) of your work (remember to include your names!). Discuss: * The problems you all saw with the original code * What new Ruby you had to learn to accomplish this * What does your improved code looks like? * In what ways do you feel it’s a solution?

Here’s the code:

 1class MountainBike
 2
 3  TIRE_WIDTH_FACTOR = 250
 4  FRONT_SUSPENSION_FACTOR = 100
 5  REAR_SUSPENSION_FACTOR = 150
 6
 7  def initialize(params)
 8    params.each { |key, value| instance_variable_set "@#{key}", value }
 9
10    @commission = 0.25
11    @front_suspension_price = 95.0
12    @rear_suspension_price = 67.0
13    @base_price = 490.00
14  end
15
16  def off_road_ability
17    result = @tire_width * TIRE_WIDTH_FACTOR
18    if @type_code == :front_suspension || @type_code == :full_suspension
19      result += @front_fork_travel * FRONT_SUSPENSION_FACTOR
20    end
21    if @type_code == :full_suspension
22      result += @rear_fork_travel * REAR_SUSPENSION_FACTOR
23    end
24    result
25  end
26  
27  def price
28    case @type_code
29    when :rigid
30      (1 + @commission) * @base_price
31    when :front_suspension
32      (1 + @commission) * @base_price + @front_suspension_price
33    when :full_suspension
34      (1 + @commission) * @base_price + @front_suspension_price +
35        @rear_suspension_price
36    end
37  end
38
39  def owner
40    @owner
41  end
42
43  def to_s
44    "Mountain bike - owner: #{owner}, off road ability: #{off_road_ability()}, price: #{price}"
45  end
46
47end
48
49pitos_bike = MountainBike.new(:owner => "Pito", :type_code => :rigid, :tire_width => 2.5)
50puts pitos_bike
51
52ricks_bike = MountainBike.new(:owner => "Rick", :type_code => :front_suspension, :tire_width => 2, :front_fork_travel => 3)
53puts ricks_bike
54