Nic Lin's Blog

喜歡在地上滾的工程師

Class method 氾濫帶來什麼問題

因為 Class method 是 global method 所以可以到處被使用

就像 User.all 這樣的方法可以在 model, controller, view 都被使用

Class method 有什麼問題?

代表你可以隨處使用而不必在乎前後因果

class User
  def self.email_to(id, email)
    user = User.find(id)
    Emailer.send(email, "Hello, #{user.name}")
  end

下面這是更好一點的版本,因為寄信這件事情應該可以直接針對該 user,也就不用在去 find 了

class User
  def email_to(email)
    Emailer.send(email, "Hello, #{name}")
  end

這樣一來你在別處執行的 code 可能就會變成

user = User.find(params[:id])

user.email_to(email)

而不是 class method

User.email_to(params[:id], email)

因為 class method 不是在一個 object instance 上面操作,是 global 都能使用,這其實不太符合 OOP 精神,尤其 ruby 又是這麼 OOP 的一個語言(萬物皆為 object)。

Class method 的寫法比較像是程序式程式語言(Procedural programming) 然而 instance method 在 object 創建本身就需要帶完整和正確的參數,在 debug 上有一定的優勢。

我個人比較偏好寫 instance method,因為 object instance 再做完他的工作就死了,不會有污染的問題…

所謂污染就是把 object 能做的事情丟給 global 做就會變成這樣

# 應該要用 class 的 new 去做,但自己寫了 initialize?

module Hello
  def self.initialize(first_name, last_name)
    @@first_name = first_name
    @@last_name = last_name
  end

  def self.fullname
    "#{@@first_name} #{@@last_name}"
  end
end

# 假設有一個 class 要使用 Hello module
class Cool
  def boy
    # 這時候強制設定了 class 層級的 local variable
    Hello.initialize("Nic", "Lin")

    puts Hello.fullname
  end

  def girl
    # 然後又把 local variable 覆蓋了...
    Hello.initialize("Doris", "Lin")

    puts Hello.fullname
  end
end

# 執行
example_1 = Cool.new

example_1.boy
# Nic Lin

example_1.girl
# Doris Lin

執行起來沒問題,但是在這個層級裡面,你的 Hello module 其實偷偷覆寫了 local variable,這是一個非常危險的事情。

我知道這 example 看起來很蠢,但確實存在。

小結

如果不是可以全域使用的方法,我認為還是用 instance method 好一些,尤其很多 service 的邏輯封裝,更不適合這樣污染。

並不是說 class method 不好,只是覺得如果要運用不該是到處氾濫,畢竟我們很常用一些好用的 class method 例如 User.find, User.all

參考資料

comments powered by Disqus