2010-07-01 3 views
3

제 애플리케이션에는 많은 수업이 있습니다. 이 클래스의 대부분은 꽤 많은 데이터를 저장합니다. 데이터 클래스 중 하나의 내용이 변경되면 내 응용 프로그램의 다른 모듈도 '업데이트'되는 것이 중요합니다.데이터 변경을 관찰하는 여러 가지 방법

이 작업을 수행하는 일반적인 방법은 다음과 같이이다 : 회원이 자주 변경되지 않고 '관찰 클래스'필요로하는 경우 이것은 매우 좋은 방법입니다

void MyDataClass::setMember(double d) 
{ 
m_member = d; 
notifyAllObservers(); 
} 

- - 날짜 빨리 가능한 한.

의 변화를 관찰하는 또 다른 방법은 이것이다 :

void MyDataClass::setMember(double d) 
{ 
setDirty(); 
m_member = d; 
} 

이 회원이 여러 번 변경되는 경우 좋은 방법, 전혀 정기적으로 '더러운'인스턴스에서 '관찰 클래스'모습이다.

불행히도, 내 수업에는 두 가지 종류의 데이터 멤버가 혼합되어 있습니다. 일부는 자주 변경되지 않고 (정상적인 관찰자와 함께 살 수 있습니다.) 다른 것들은 여러 번 여러 번 변경됩니다 (이는 복잡한 수학 알고리즘 내에 있음). 그리고 값 변경이있을 때마다 관찰자를 호출하면 응용 프로그램의 성능이 저하됩니다.

데이터 변경을 관찰하는 다른 방법이나 데이터 변경을 관찰하는 여러 가지 다른 방법을 쉽게 결합 할 수있는 패턴이 있습니까?

이 언어는 다소 언어 독립적 인 질문이지만 (다른 언어로 된 예제를 이해하려고 시도 할 수도 있음) 최종 해결책은 C++에서 작동해야합니다.

답변

4

당신이 묘사 한 두 가지 방법은 두 가지 측면을 (개념적으로) 다루지 만, 당신이 충분히 장단점을 설명하지는 않았다고 생각합니다.

주의해야 할 항목이 하나 있습니다. 인구 계수입니다. 많은 신고자 거의 관찰자가있을 때

  • 푸시 방법은 중대하다
  • 풀 방법은 몇 신고자 많은 관찰자

당신이 많은 신고자가있는 경우가있을 때 위대하고 관찰자가 가정된다 그들 모두를 반복하여 2 또는 3을 발견하면 dirty ... 작동하지 않습니다. 반면에 많은 옵서버가 있고 업데이트 할 때마다 모두에게 알릴 필요가 있다면 단순히 모든 작업을 반복하면 성능이 저하 될 것이므로 아마 운명을 저 지르게 될 것입니다.

그러나 두 가지 방법을 다른 수준의 간접 지정과 결합하여 이야기하지는 않았을 가능성이 있습니다. 그것은 비록 그 쉬운 일이 아니다

을 필요할 때마다 관찰자가 지난번 때 기억해야하기 때문에

  • 를 밀어 낼 GlobalObserver
  • 에 대한 모든 변화는의 GlobalObserver 각 관찰자 체크를해야 아직 관찰되지 않은 변경 사항에 대해서만 통지받을 수 있습니다. 일반적인 트릭은 신기원을 사용하는 것입니다.

    Epoch 0  Epoch 1  Epoch 2 
    event1  event2  ... 
    ...   ... 
    

    각 관찰자는 (관찰자는 그 대가로 현재의 시대가 주어집니다 구독하는 경우) 읽을 필요가 다음 시대를 기억하고, 모든 이벤트 알 수있는 현재까지이 시대에서 읽습니다. 일반적으로 현재 에노크는 알리미에 의해 액세스 될 수 없으며, 예를 들어 읽기 요청이 도착할 때마다 (현재 에포크가 비어 있지 않은 경우) 에포크를 전환하도록 결정할 수 있습니다.

    여기에서의 어려움은 에포크를 폐기해야하는시기 (더 이상 필요없는 경우)를 아는 것입니다. 여기에는 일종의 참조 계산이 필요합니다. GlobalObserver은 현재 에포크를 객체로 반환하는 것입니다. 그래서 우리는 각 신기원에 대해 얼마나 많은 관측자가이 신기원 (및 후속의 것들)을 아직 관찰하지 않았는지를 세는 카운터를 소개합니다.가입에

    • , 우리는 시대의 수를 반환하고이 시대 폴링에
    • 의 카운터를 증가, 우리는 폴링 신기원의 카운터를 감소 및 탈퇴에 자사의 카운터
    • 를 현재 시대의 수를 반환하고 증가 , 우리는 신기원의 카운터를 줄입니다. -> 소멸자가 구독을 취소했는지 확인하십시오!

    마지막으로 시간대 수정 (즉, 다음 시간대 생성)을 등록하고 일정 시간이 지나면 폐기 할 수 있음을 결정할 수 있습니다 (이 경우 우리는 카운터를 계산하여 다음 에포크에 추가하십시오).

    하나의 에포크는 쓰기 (스택에서 푸시 작업)에 액세스 할 수 있고 다른 항목은 읽기 전용 (원자 카운터 제외)이기 때문에 멀티 스레딩으로 확장됩니다. lock-free 연산을 사용하여 메모리를 할당 할 필요가없는 조건에서 스택을 푸시 할 수 있습니다. 스택이 완료되면 신기원을 전환하기로 결정하는 것은 제정신이 아닙니다.

+0

인상적입니다. +1. – Patrick

+0

동일한 주제에 관한 새로운 질문 (Patrick) : http://stackoverflow.com/questions/3667317/best-way-to-keep-the-user-interface-up-to-date 많은 사건이 공연을 죽일 수 있습니다. –

0

사용할 수있는 두 가지 고급 옵션 (푸시/폴링 비교)에 대해 설명했습니다. 내가 아는 다른 옵션은 없습니다. 데이터를 관찰

4

다른 트릭은 정말

하지 않은 변경. 디자인 패턴을 "밀고"당깁니다. 다른 선택 사항은 없습니다.

notifyAllObservers는 푸시이고, 통상의 액세스 속성은 손잡이이다.

일관성을 권장합니다. 분명히 개체에 많은 변경 사항이 있지만 상황이 달라졌습니다. 모두 변경 사항이 다른 개체로 통과되지 않습니다.

이것으로 혼동하지 마십시오.

관찰자는 변경 사실을 알았 기 때문에 값 비싼 계산을 수행 할 필요가 없습니다.

"빈번한 변경이지만 느린 요청"클래스를 처리하려면 이와 같은 클래스가 있어야한다고 생각합니다.

class PeriodicObserver { 
    bool dirty; 
    public void notification(...) { 
     // save the changed value; do nothing more. Speed matters. 
     this.dirty= True; 
    } 
    public result getMyValue() { 
     if(this.dirty) { 
      // recompute now 
     } 
     return the value 
} 
2

사용자는 당김 및 밀기 알림을 가지고 있습니다.적어도 신고자가의 차이에 대해 신경 쓸 필요가없는, 그래서 나는 가능한 한 세부 사항을 숨기려고 생각 하는데요 :

class notifier { 
public: 
    virtual void operator()() = 0; 
}; 

class pull_notifier : public notifier { 
    bool dirty; 
public: 
    lazy_notifier() : dirty(false) {} 
    void operator()() { dirty = true; } 
    operator bool() { return dirty; } 
}; 

class push_notifier : public notifier { 
    void (*callback)(); 
public: 
    push_notifier(void (*c)()) : callback(c) {} 
    void operator()() { callback(); } 
}; 

그런 다음 관찰자가 통과 할 수 중 하나 push_notifier 또는 pull_notifier 그것을보고로 에 맞게, 그리고 뮤 테이터 차이에 대해 신경 쓸 필요가 없습니다 :

class MyDataClass { 
    notifier &notify; 
    double m_member; 
public: 
    MyDataClass(notifier &n) : n_(n) {} 
    void SetMember(double d) { 
     m_member = d; 
     notify(); 
    } 
}; 

내가 뮤 테이터 당 하나의 관찰자와 함께 쓴 순간,하지만 포인터의 벡터에 그 변경 상당히 간단합니다 만약 당신이 더 많은 것을 원한다면 주어진 뮤 테이터에 대한 객체를 관찰하는 것. 이를 통해 주어진 뮤 테이터는 push_ 및 pull_ 통지 자의 임의 조합을 지원합니다. 주어진 mutator가 pull_notifier 또는 push_notifiers 만 사용한다고 확신하는 경우 가상 함수 호출의 오버 헤드를 피하기 위해 템플릿 매개 변수 (정책)로 알리미가있는 템플릿을 사용하는 것이 좋습니다 (push_notifier, 하지만 덜 pull_notifier).

관련 문제