Get a Whiff of This

日本語あらすじと解説

https://rastamhadi.github.io/get_a_whiff_of_this

Get a Whiff of This

https://speakerdeck.com/skmetz/get-a-whiff-of-this
http://confreaks.tv/videos/railsconf2016-get-a-whiff-of-this

Practical Object-Oriented Design in Ruby   オブジェクト指向設計実践ガイド

Sandi Metz

https://www.sandimetz.com/

Get a Whiff of This

これ…

ちょっと嗅いでみ?

:nose:

Refactoring   リファクタリング

Kent Beck

Long Method Large Class Comments
Message Chains Data Clumps Lazy Class
Temporary Field Refused Bequest Data Class
Divergent Change Switch Statements Middle Man
Duplicate Code Speculative Generality Feature Envy
Shotgun Surgery  
Inappropriate Intimacy Long Parameter List
Primitive Obsession Incomplete Library Client
Parallel Inheritance Hierarchies Alternative Classes with Different Interfaces

Subjective evaluation of software evolvability using code smells: An empirical study

Mäntylä, M. V. and Lassenius, C.
“Subjective Evaluation of Software Evolvability
Using Code Smells: An Empirical Study”.

Journal of Empirical Software Engineering,
vol. 11, no. 3, 2006, pp. 395-431

Bloaters
Tool Abusers
Change Preventers
Dispensables
Couplers

Bloaters

Large Class
Long Method
Long Parameter List
Primitive Obsession
Data Clumps

Tool Abusers

Alternative Classes with Different Interfaces
Switch Statements
Refused Bequest
Temporary Field

Change Preventers

Parallel Inheritance Hierarchies
Divergent Change
Shotgun Surgery

Dispensables

Speculative Generality
Duplicate Code
Data class
Lazy class

Couplers

Inappropriate Intimacy
Message Chains
Feature Envy
Middle Man

臭い?

:hankey:

class Foo
  def sales_total(params)
    start_date = Date.parse(params[:starting])
    end_date   = Date.parse(params[:ending])

    Sale.where(date: start_date..end_date).sum(:cost)
  end
end
class Bar
  def weekly_sales_total(params)
    start_date = Date.parse(params[:starting])
    end_date   = start_date + 6.days

    Sale.where(date: start_date..end_date).sum(:cost)
  end
end
class Baz
  def expense_total(params)
    start_date = Date.parse(params[:starting]) rescue Date.today
    end_date   = Date.parse(params[:ending])   rescue start_date

    Expense.where(date: start_date..end_date).sum(:cost)
  end
end

Data Clump

→ Extract Class

class DateRange
  def initialize(starting, ending: nil)
    @starting = Date.parse(starting) rescue Date.today
    @ending   = Date.parse(ending)   rescue @starting
  end

  def range
    @starting..@ending
  end

  def week_range
    @starting..(@starting + 6)
  end
end
class Foo
  def sales_total(params)
    range = DateRange.new(params[:starting], params[:ending]).range

    Sale.where(date: range).sum(:cost)
  end
end
class Bar
  def weekly_sales_total(params)
    range = DateRange.new(params[:starting]).week_range

    Sale.where(date: range).sum(:cost)
  end
end
class Baz
  def expense_total(params)
    range = DateRange.new(params[:starting], params[:ending]).range

    Expense.where(date: range).sum(:cost)
  end
end

Message Chain

→ Hide Delegate

class Sale < Persistance
  def self.total(within:)
    where(date: within).sum(:cost)
  end
end
class Expense < Persistance
  def self.total(within:)
    where(date: within).sum(:cost)
  end
end
class Foo
  def sales_total(params)
    range = DateRange.new(params[:starting], params[:ending]).range

    Sale.total(within: range)
  end
end
class Bar
  def weekly_sales_total(params)
    range = DateRange.new(params[:starting]).week_range

    Sale.total(within: range)
  end
end
class Baz
  def expense_total(params)
    range = DateRange.new(params[:starting], params[:ending]).range

    Expense.total(within: range)
  end
end
class Foo
  def sales_total(params, model = Sale)
    range = DateRange.new(params[:starting], params[:ending]).range

    model.total(within: range)
  end
end
class Bar
  def weekly_sales_total(params, model = Sale)
    range = DateRange.new(params[:starting]).week_range

    model.total(within: range)
  end
end
class Baz
  def expense_total(params, model = Expense)
    range = DateRange.new(params[:starting], params[:ending]).range

    model.total(within: range)
  end
end
class TotalableDouble
  def self.total(within:)
    47
  end
end
class FooTest < MiniTest::Test
  def test_sales_total
    params = { starting: '2016-04-01', ending: '2016-04-07' }
    assert_equal 47, Foo.new.sales_total(params, TotalableDouble)
  end
end
class Sale < Persistance
  def self.total(within:)
    where(date: within).sum(:cost)
  end
end
class Expense < Persistance
  def self.total(within:)
    where(date: within).sum(:cost)
  end
end

Duplicate Code

→ Pull Up Method

class Totalable
  def total(within:)
    where(date: within).sum(:cost)
  end
end
class Sale < Persistance
  extend Totalable
end
class Expense < Persistance
  extend Totalable
end
class Totalable
  def total(within:, date_field: :date, on: :cost)
    where(date_field => within).sum(on)
  end
end

Speculative Generality

:scream:

class Totalable
  def total(within:, date_field: :date, on: :cost)
    where(date_field => within).sum(on)
  end
end

Reek

Get a Whiff of This

日本語あらすじと解説

https://rastamhadi.github.io/get_a_whiff_of_this