메서드 호출 결과를 저장한다
스프링노트 소스 코드에서 자주 보이는 루비 관용 표현 중 하나는 이런 것이다.
- 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 메서드를 참조하기 바란다.
이 글은 스프링노트에서 작성되었습니다.
History
Last edited on 09/04/2007 11:27 by deepblue
Comments (0)