2011-02-01 5 views
10

현재 제가 작업하고있는 응용 프로그램에서 많은 관찰자가 나타납니다. 이것은 참으로 나를 위해 많은 문제를 일으키고 있습니다. 코드를 변경하고 새로운 기능을 추가하는 동안 이러한 관찰자가 부작용을 유발합니다.레일 옵저버 - 레일에서 옵저버를 사용하지 않을 때

Observer를 요구하는 경우를 알고 싶습니다. 사람들이 관찰자 함정에 빠지려고 할 때 경험적 또는 개인적 경험이 필요합니다.

귀중한 경험, 전쟁 이야기 및 생각은 수요가 있습니다. 제발 소리 치십시오!

+0

Observers 대상의 경우에는 (비즈니스 논리) 단계 인 일련의 [비즈니스 사례] (http://webuild.envato.com/blog/a-case-for-use-cases/)를 사용하는 것이 좋습니다. 실행. 자신의 유스 케이스를 굴리기 란 어렵지 않지만 괜찮은 보석도 있습니다. [solid_use_case] (https://github.com/mindeavor/solid_use_case) 또는 [use_case] (https://github.com/cjohansen/use_case) – Dennis

답변

28

사람들이 ActiveRecord 라이프 사이클 콜백을 사용하여 동일한 일로 덩치가 크다고 느끼고 있습니다. 라이프 사이클 콜백에 대한 대중의 많은 의견이 오용되기 쉽다는 것에 동의하지만, 개인적으로는 특정 모델의 핵심 책임이 아닌 모델 클래스에서 물건을 지키려는 관찰자의 큰 팬입니다. 여기에 힌트가 있습니다. 레일스 관측자는 부분적으로 방향 기반 프로그래밍에 의해 부분적으로 영향을 받았는데, 이는 크로스 커팅 문제에 관한 것입니다. 관찰중인 모델과 긴밀하게 결합 된 옵저버에 비즈니스 로직을 넣는다면 잘못된 IMO를 수행하고있는 것입니다.

그들은있어 사용자 분석 이벤트, 따뜻한 캐시 등을 추적 할 수있는 백그라운드 작업을 캐시 만료 (스위퍼), 다양한 종류의 알림, 활동 스트림 업데이트와 같은 모델 클래스에서 혼란을 유지 발로 이상적

관찰자가 단위 테스트를 제대로 수행하기 어렵다는 BlueFish의 주장에 동의하지 않습니다. 이것이 수명주기 콜백과 구별되는 가장 중요한 포인트입니다. 관찰자를 개별적으로 테스트 할 수 있기 때문에 BlueFish가 언급하는 많은 국가 및 주문 디자인 함정에 빠지지 않도록 할 수 있습니다 (다시 생각해보십시오. 라이프 사이클 콜백의 경우).

여기 내 처방이다 :

  1. 사용 안 함 기본적하여 테스트 스위트의 모든 관찰자. 그들은 어쨌든 별도의 우려를 가져야하기 때문에 모델 테스트를 복잡하게하지 않아야합니다. ActiveRecord의 테스트 스위트가이를 수행하기 때문에 관찰자가 실제로 발생시키는 단위 테스트는 필요하지 않으며 통합 테스트가이를 수행합니다. 단위 테스트의 일부 작은 부분에 대해 관찰자를 활성화해야하는 충분한 이유가 있다고 생각하면 블록 양식 ActiveRecord::Base.observers.enable을 사용하십시오. 그러나 오용 또는 디자인 문제의 가능성이 있습니다.
  2. 통합 테스트에 대한 옵서버 만 사용 가능. 통합 테스트는 풀 스택이어야하며 다른 모든 것들처럼 관찰자의 행동을 검증해야합니다.
  3. 관찰자 클래스를 단위 테스트로 고립 시키십시오. (after_create과 같은 메소드를 직접 호출하십시오). 관찰자가 관찰 된 모델의 비즈니스 로직의 일부가 아닌 경우 모델 인스턴스의 상태 세부 정보에 크게 의존하지 않을 것이며 많은 테스트 설정이 필요하지 않습니다. 통합 테스트에서 가장 신경 쓰는 부분을 충분히 확신 할 수 있다면 여기에서 공동 작업자를 조롱 할 수 있습니다.나는 희망

    RSpec.configure do |config| 
        # Assure we're testing models in isolation from Observer behavior. Enable 
        # them explicitly in a block if you need to integrate against an Observer -- 
        # see the documentation for {ActiveModel::ObserverArray}. 
        config.before do 
        ActiveRecord::Base.observers.disable :all 
        end 
    
        # Integration tests are full-stack, lack of isolation is by design. 
        config.before(type: :feature) do 
        ActiveRecord::Base.observers.enable :all 
        end 
    end 
    

    그리고 here is a real-world example은 관찰자를 사용하여 통증없이 테스트를위한 좋은 사례를 보여

여기 RSpec에를 사용하여 응용 프로그램 내 표준 상용구 spec/support/observers.rb입니다.

+0

큰 대답! 나는 너에게 동의한다. 그리고이 질문에 대답 해 주셔서 감사합니다 :-) – karthiks

+0

답변에 언급 된 많은 문제를 해결하는 Wisper gem (https://github.com/krisleech/wisper)을 제안 해주십시오. – Kris

6

이럴 - 관찰자 내가 그들이 생각하는 이유를 여러 가지 이유를 통해 갈 것이다

를 빨아. before_x 또는 after_x 메소드의 사용에도 일반적으로 적용됩니다. 일반적인 Observer의 더 작은 예입니다.

은 하드, 적절한 단위 테스트 일반적으로

를 작성하는 단위 테스트를 작성할 때, 당신은 기능의 특정 부분을 테스트 만듭니다. 그러나 관찰자를 테스트하려면 이벤트를 테스트하기 위해 이벤트를 트리거해야하며 때로는 매우 불편할 수도 있습니다.

예. 옵저버를 before_save에 연결 한 다음 코드를 트리거하려면 모델을 저장해야합니다. 이것은 당신이 끈기가 아닌 비즈니스 로직을 테스트 할 수 있기 때문에 테스트하기가 까다 롭습니다. 저장을 조롱하면 트리거가 작동하지 않을 수 있습니다. 그리고 당신이 그것을 구할 수 있다면, 당신의 시험은 느립니다.

상태

관찰자 테스트하기 어려운 경향이 있다는 사실에 이어이 필요, 관찰자는 많은 상태를 요구하는 경향이있다. 그 이유는 관찰자의 논리가 다양한 '비즈니스 이벤트'를 구별하려고하기 때문이며, 그렇게하는 유일한 방법은 객체의 상태를 보는 것입니다. 이를 위해서는 테스트에 많은 설정이 필요하므로 테스트가 어렵고 지루하고 문제가됩니다.

의도하지 않은 결과

의심의 여지가, 당신은 단지 여러 관찰를 연결 할 수 있기 때문에, 다양한 행동을 유발 할 수있는 것을 아무 생각이 없다는 것을 경험했다. 이로 인해 의도하지 않은 결과가 발생합니다. 통합/시스템 테스트 (느린 피드백)를 통해서만 선택할 수 있습니다. 당신의 관찰자를 추적하는 것도 많은 즐거움이 아닙니다.

주문 가정 문제

관찰자가. 당신은 단지 그것을 쫓겨 될 것이라고 보장 킥 수 있습니다 보장이 없습니다. 비즈니스 규칙의 일부로 암시 적 질서가있는 경우 옵저버가 잘못되었습니다.

가난한 설계로 연결 관찰자를 접선으로 가난한 디자인

일을 추가로 리드. 그것은 당신이 편리하고도 이해하기 어려운 이벤트를 저장, 삭제, 생성하기 위해 모든 것을 연결하도록 이끌어가는 경향이 있습니다. 예 : 사용자를 저장하면 사용자의 세부 정보를 업데이트 할 수도 있고 새 계정 이름을 추가 할 수도 있습니다.특정 대상에 대해 수행 할 수있는 작업을 파악하는 것이 방법과 의미있는 작업 기반 이름이있는 이유 중 일부입니다. 모든 것이 관찰자라면, 이것은 잃어 버리게되고 모든 것이 이벤트에 응답하고, 관찰 논리 내에서 어떤 비즈니스 이벤트에 속하는 이벤트를 구별하는 경향이 있습니다.

관찰자가 좋은 곳이 있지만 일반적으로 예외입니다. 콜백을 통해 로직을 암묵적으로 인코딩하는 것이 아니라 명시 적으로 수행 할 수있는 것을 이식하는 것이 훨씬 낫습니다.

5

나는 관찰자가 불필요한 복잡성을 초래할 수 있다는 점에서 부분적으로 BlueFish에 동의하지만 관찰자는 관심사를 대상에서 분리하는 데 유용합니다.

예를 들어 지불 AR 모델에서는 생성 후 영수증을 전달할 수 있습니다. 일반적인 AR after_create 콜백을 사용하면 deliver_receipt 메소드가 실패하면 지불이 데이터베이스에 기록되지 않습니다. oops! 그러나 관찰자에게는 지불금이 여전히 저장됩니다. 실패는 구조에 의해 처리되어야한다고 주장 할 수는 있지만 여전히 그곳에 속하지 않는다고 생각합니다. 그것은 관찰자에 속한다.

+0

흥미로운 POV. 그러나 after_create의 일부로 영수증 생성을 delayed_job으로 푸는 것이 더 좋지 않을 것입니다. 그렇게하면 관찰자는 피할 수 있고 코드도 유지 관리 할 수 ​​있습니다. – karthiks

+0

지연된 작업으로 추가하는 것은 확실히 선택 사항이지만 전자 메일을 보내는 것은 비용이 많이 드는 작업이 아니므로 IMO는 이러한 복잡성을 추가하는 것을 정당화하지 못합니다. – Zubin

+0

지불 대상에 상태 시스템이 없어야하며 불완전 → 실패 또는 불완전 → 전환 성공으로 연결해야합니까? 'state_machine'과 같은 것은 여러분이 DB/영속성 이벤트가 아닌 비즈니스 이벤트에 매핑되는 그러한 상태 변화에 대한 옵저버를 작성할 수있게합니다. –

관련 문제