메서드 호출 결과를 저장한다
July 13th, 2007
스프링노트 소스 코드에서 자주 보이는 루비 관용 표현 중 하나는 이런 것이다.
- def hard_stuff
- @cached_result_of_this_method ||= do_some_calculations
- end
같은 계산을 여러 번 하는 낭비를 막기 위한 방법이다. 뷰에서 호출하는 헬퍼 메서드들은 대부분 저런식으로 구현해서 계산 시간을 단축하도록 만들었다. 그러던 어느 날, 인스턴스 변수 이름 짓기가 귀찮아지고, 저런 반복적인 행태가 심한 중복으로 보였다. 역시 DRY를 신봉하는 루비 커뮤니티 답다. :)
그래서 같은 일을 하는데, 좀 더 선언적으로 할 수 있도록 구현했다.
- def hard_stuff
- do_some_calculations
- end
- once :hard_stuff
Object#once 메서드를 구현해 모든 객체에서 선언적으로 '한번만 호출되는 메서드'를 만들수 있도록 하였다. 아래는 스프링노트의 lib/method_once.rb 파일의 소스 코드이다.
- # 메서드 결과를 한번만 실행하도록 cache 하는 코드
- class Object
- class <<self
- def once(*methods)
- methods.each{|m| once_method(m)}
- end
- def once_method(method)
- origin = '_original_' + method.to_s
- alias_method origin, method
- define_method method do
- @_once_cache ||= {}
- return @_once_cache[method] if @_once_cache.has_key? method
- @_once_cache[method] = send(origin)
- end
- end
- end
- end
once_method는 원래 구현된 메서드(ex. hard_stuff)를 _orign_hard_stuff로 이름을 바꾸고 hard_stuff를 새로 구현하는 식이다. 그리고 once 메서드는 여러 개의 메서드 이름을 한꺼번에 받아서 처리할 수 있도록 했다.
물론 이 코드를 검증하기 위한 스펙도 있다.
- class TestClass
- def initialize(a); @a=a end
- def normal; @a+=1 end
- def abnormal; @a+=1 end
- def abnormal2; @a+=2 end
- once :abnormal, :abnormal2
- end
- describe "method once" do
- before(:each) do
- @obj = TestClass.new(100)
- end
- it "once로 선언된 메서드는 한번만 실행된다" do
- @obj.normal.should eql(101)
- @obj.normal.should eql(102)
- @obj.abnormal.should eql(103)
- @obj.abnormal.should eql(103)
- @obj.abnormal2.should eql(105)
- @obj.abnormal2.should eql(105)
- end
- end
위 메서드의 문제점은 매개변수에 따라 값이 달라지는 메서드의 캐싱이 불가능하다는 점이다. 이를 알고 있지만 구현하지 않은 이유는 액티브서포트에 훌륭한 대안이 있기 때문이다. 그 내용이 궁금한 사람은 ActiveSupport::CachingTool::HashCaching#hash_cache 메서드를 참조하기 바란다.
이 글은 스프링노트에서 작성되었습니다.




August 4th, 2007 at 03:44 PM (myRuby.net) once :hard_stuff 에서 hard_stuff도 두 번 나오니까 중복 아닌가? 오히려 선언적으로 만들면서 더 중복이 된 것 같은 느낌이 드는데.. def hard_stuff evaluate_once :do_some_calculations end 이러면 어떨런지. 파이썬이라면 @once def hard_stuff do_some_calculations end 이런 식이 되겠군. 루비도 자바의 어노테이션이나 파이썬의 데코레이터 같은 게 있으면 before_filter 같은 것도 그렇게 안 써도 될 텐데 하는 생각이…
August 4th, 2007 at 03:50 PM (myRuby.net) 아악, 줄바꿈이 안되네.
August 5th, 2007 at 03:42 AM memoization인가요?
August 5th, 2007 at 07:00 AM 영록// def_once_method 정도의 메서드를 만들면 중복이 사라질 수도 있겠군요. 재밌는 생각같습니다.