Follow the Law of Demeter

rails_antipatternsについて書いていきます

 1 class Address < ActiveRecord::Base
 2   belongs_to :customer
 3 end
 4 
 5 class Customer < ActiveRecord::Base
 6   has_one :address
 7   has_many :invoices
 8 end
 9 
10 class Invoice < ActiveRecord::Base
11   belongs_to :customer
12 end
= @invoice.customer.name
= @invoice.customer.address.street
= @invoice.customer.address.city
= @invoice.customer.address.state
= @invoice.customer.address.zip_code

AntiPattern

  • streetcityを取得するためにcustomerオブジェクトをまたぐ記法で記述すべきでない

可読性(readability)、保守性(maintainability)の観点であまりよろしくない。customerオブジェクトをまたぐのはあくまでstreetcityを取得する手段であり、コードを読み進めていく上で必ずしも必要な情報ではない。またcustomerbilling addressshipping addressの両方を持つようになった場合、修正箇所が多くなる。同時に記述量も多くなる

Solution the Law of Demeter

ここでthe Law of Demeterですよ。Law of Demeterは1987年にNortheastern Universityで提唱された概念で「AオブジェクトがBオブジェクトのmethodをcallするために、Bオブジェクトにアクセスすべきではない」というもので簡単に言うと「.は一回しか使うな」ってこと

Follow the Law of Demeter

1 class Address < ActiveRecord::Base
2   belongs_to :customer
3 end
 1 class Customer < ActiveRecord::Base has_one :address
 2   has_many :invoices
 3 
 4   def street
 5     address.street
 6   end
 7 
 8   def city
 9     address.city
10   end
11 
12   def state
13     address.state
14   end
15 
16   def zip_code
17     address.zip_code
18   end
19 end
 1 class Invoice < ActiveRecord::Base belongs_to :customer
 2   def customer_name
 3     customer.name
 4   end
 5 
 6   def customer_street
 7     customer.street
 8   end
 9 
10   def customer_city
11     customer.city
12   end
13 
14   def customer_state
15     customer.state
16   end
17 
18   def customer_zip_code
19     customer.zip_code
20   end
21 end
= @invoice.customer_name
= @invoice.customer_street
= @invoice.customer_city
= @invoice.customer_state
= @invoice.customer_zip_code

これじゃModelがcolumnのmethodだらけになって膨れてく

Use delegate of rails

1 class Address < ActiveRecord::Base
2   belongs_to :customer
3 end
1 class Customer < ActiveRecord::Base
2   has_one :address
3   has_many :invoices
4 
5   delegate :street, :city, :state, :zip_code, :to => :address
6 end
 1 class Invoice < ActiveRecord::Base
 2   belongs_to :customer
 3 
 4   delegate :name,
 5            :street,
 6            :city,
 7            :state,
 8            :zip_code,
 9            :to => :customer, :prefix => true
10 end
= @invoice.customer_name
= @invoice.customer_street
= @invoice.customer_city
= @invoice.customer_state
= @invoice.customer_zip_code

ちょーきもちい



© 2015 kyuden