안 드 로 이 드 디자인 모델 학습 의 관찰자 모델

관찰자 모델 은 실제 프로젝트 에서 도 매우 자주 사용 되 는데 가장 많이 사용 되 는 곳 은 GUI 시스템,구독-발표 시스템 등 이다.이 모델 의 중요 한 역할 은 바로 결합 을 해제 하 는 것 이기 때문에 그들 간 의 의존성 을 더욱 작 게 하고 심지어 의존 하지 않 게 하 는 것 이다.GUI 시스템 으로 볼 때 응 용 된 UI 는 변화성 을 가진다.특히 초기 에 업무 의 변화 나 제품 의 수요 에 따라 수정 되 었 고 응용 인터페이스 도 자주 변화 했다.그러나 업무 논리의 기본 적 인 변화 가 크 지 않다.이때 GUI 시스템 은 이러한 상황 에 대응 하 는 체제 가 필요 해서 UI 층 과 구체 적 인 업무 논리 가 결합 되 고 관찰자 모델 은 이때 도움 이 된다.
개술
관찰자 모델 은 게시/구독 모델 이 라 고도 부 르 는데 관찰자 모델 은 한 쌍 의 다 중 의존 관 계 를 정의 하여 여러 관찰자 대상 이 특정한 주제 대상 을 동시에 감청 하도록 한다.이 테마 대상 은 상태 가 변 할 때 모든 관찰자 대상 에 게 자동 으로 자신 을 업데이트 할 수 있 도록 알려 줍 니 다.
패턴 속 캐릭터

추상 적 인 주제(Subject):모든 관찰자 대상 의 인용 을 하나의 집합 에 저장 하고 모든 주제 에 모든 수량의 관찰자 가 있 을 수 있 습 니 다.추상 적 인 테 마 는 관찰자 대상 을 추가 하고 삭제 할 수 있 는 인 터 페 이 스 를 제공 합 니 다.
구체 적 인 주제(Concrete Subject):관련 상 태 를 구체 적 인 관찰자 대상 에 저장 합 니 다.구체 적 인 주제 내부 상태 가 바 뀌 었 을 때 등 록 된 모든 관찰자 에 게 알림 을 보 냅 니 다.
추상 관찰자(Observer):모든 구체 적 인 관찰자 에 게 인 터 페 이 스 를 정의 하고 테마 알림 을 받 을 때 자신 을 업데이트 합 니 다.
구체 적 인 관찰자(ConcreteObserver):추상 적 인 관찰자 역할 이 요구 하 는 업데이트 인 터 페 이 스 를 실현 하여 자신의 상태 와 주제 상 태 를 조 화 롭 게 한다.
1.Subject 와 Observer 는 한 쌍 이상 의 관계 이다.즉,관찰 자 는 Observer 인 터 페 이 스 를 실현 하고 자신 을 Subject 에 등록 하면 메시지 이 벤트 를 받 을 수 있다.
2.자바 API 는 내 장 된 관찰자 모드 클래스 가 있 습 니 다.자바 util.Observable 클래스 와 자바 util.Observer 인터페이스 입 니 다.이것 은 각각 Subject 와 Observer 의 역할 에 대응 합 니 다.
3.자바 API 를 사용 하 는 관찰자 모드 클래스 입 니 다.주의해 야 할 것 은 피 관찰자 가 notify Observer()함수 로 관찰자 에 게 알 리 기 전에 setChanged()함 수 를 호출 해 야 합 니 다.그렇지 않 으 면 관찰자 가 통 지 를 받 을 수 없습니다.
4.자바 API 를 사용 하 는 단점 도 뚜렷 합 니 다.Observable 은 하나의 클래스 이기 때문에 자바 가 단일 계승 만 허용 하 는 단점 으로 인해 다른 부모 클래스 의 속성 을 동시에 얻 으 려 면 어댑터 모드 나 내부 클래스 만 선택 할 수 있 습 니 다.또한 setChanged()함수 가 proctected 속성 이기 때문에 Observable 클래스 를 계승 하지 않 는 한,그렇지 않 으 면 이런 속성 을 사용 할 수 없다.이것 은 디자인 모델 의 원칙 에 위배 된다.조합 을 많이 사용 하고 계승 을 적 게 사용 하 는 것 이다.
관찰자 모드 예시
MyPerson 은 피 관찰자

public class MyPerson extends Observable {

  private String name;
  private int age;
  private String sex;

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
    setChanged();
    notifyObservers();
  }

  public int getAge() {
    return age;
  }

  public void setAge(int age) {
    this.age = age;
    setChanged();
    notifyObservers();
  }

  public String getSex() {
    return sex;
  }

  public void setSex(String sex) {
    this.sex = sex;
    setChanged();
    notifyObservers();
  }

  @Override
  public String toString() {
    return "MyPerson [name=" + name + ", age=" + age + ", sex=" + sex + "]";
  }
}

setChanged();데이터 변경 을 알 리 고 notifyObserver()를 통 해 알 립 니 다.관찰자 에 게 신 호 를 보내다.
MyObserver 는 관찰자 입 니 다.

public class MyObserver implements Observer {

  private int id;
  private MyPerson myPerson;

  public MyObserver(int id) {
    System.out.println("     ---->" + id);
    this.id = id;
  }

  public int getId() {
    return id;
  }

  public void setId(int id) {
    this.id = id;
  }

  public MyPerson getMyPerson() {
    return myPerson;
  }

  public void setMyPerson(MyPerson myPerson) {
    this.myPerson = myPerson;
  }

  @Override
  public void update(Observable observable, Object data) {
    System.out.println("   ---->" + id + "    ");
    this.myPerson = (MyPerson) observable;
    System.out.println(((MyPerson) observable).toString());
  }

}

관찰자 가 알림 을 받 은 후 update 방법 으로 업데이트 작업 을 진행 합 니 다.

public class MainActivity extends Activity {

  private Button btnAddObserver;
  private Button btnChangeData;
  private MyPerson observable;
  private MyObserver myObserver;
  private List<MyObserver> myObservers;
  private ListView listview;

  private int i;

  private Handler handler = new Handler() {
    public void handleMessage(android.os.Message msg) {
      MyListAdapter myListAdapter = new MyListAdapter(MainActivity.this,
          myObservers);
      listview.setAdapter(myListAdapter);

    };
  };

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    btnAddObserver = (Button) findViewById(R.id.btn_add_observer);
    btnChangeData = (Button) findViewById(R.id.btn_change_data);
    listview = getListView();

    observable = new MyPerson();
    myObservers = new ArrayList<MyObserver>();

    btnAddObserver.setOnClickListener(new OnClickListener() {

      @Override
      public void onClick(View v) {
        myObserver = new MyObserver(i);
        i++;
        observable.addObserver(myObserver);
        myObservers.add(myObserver);
        handler.sendEmptyMessage(0);
      }
    });

    btnChangeData.setOnClickListener(new OnClickListener() {

      @Override
      public void onClick(View v) {
        observable.setName("a" + i);
        observable.setAge(10 + i);
        observable.setSex(" " + i);
        handler.sendEmptyMessage(0);
      }
    });

  }

  @Override
  protected void onDestroy() {
    // TODO Auto-generated method stub
    super.onDestroy();
    observable.deleteObserver(myObserver);
  }
}

Android 소스 코드 의 모드 구현
예전 에 우리 가 가장 자주 사용 하 는 컨트롤 은 ListView 였 는데 ListView 의 가장 중요 한 점 은 Adapter 였 다.우리 가 ListView 에 데 이 터 를 추가 한 후에 우 리 는 모두 하나의 방법 을 호출 했다.notifyDataSetChanged().이 방법 은 바로 우리 가 말 한 관찰자 모드 를 사용 한 것 이다.
이 방법 을 따라 notifyDataSetChanged 방법 입 니 다.이 방법 은 BaseAdapter 에 정의 되 어 있 습 니 다.코드 는 다음 과 같 습 니 다.

public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {
  //       
  private final DataSetObservable mDataSetObservable = new DataSetObservable();

  //     

  public void registerDataSetObserver(DataSetObserver observer) {
    mDataSetObservable.registerObserver(observer);
  }

  public void unregisterDataSetObserver(DataSetObserver observer) {
    mDataSetObservable.unregisterObserver(observer);
  }

  /**
   * Notifies the attached observers that the underlying data has been changed
   * and any View reflecting the data set should refresh itself.
   *                
   */
  public void notifyDataSetChanged() {
    mDataSetObservable.notifyChanged();
  }
}

데이터 가 변 할 때 notifyDataSetChanged 에서 mDataSetObservable.notifyChanged()방법 을 호출 하 는 것 을 발견 할 수 있 습 니 다.

public class DataSetObservable extends Observable<DataSetObserver> {
  /**
   * Invokes onChanged on each observer. Called when the data set being observed has
   * changed, and which when read contains the new state of the data.
   */
  public void notifyChanged() {
    synchronized(mObservers) {
      //         onChanged  
      for (int i = mObservers.size() - 1; i >= 0; i--) {
        mObservers.get(i).onChanged();
      }
    }
  }

}

mDataSetObservable.notifyChanged()에서 모든 관찰 자 를 옮 겨 다 니 며 onChanged 방법 을 호출 합 니 다.
그렇다면 이 관찰자 들 은 어디에서 왔 을 까?우선 ListView 는 setAdapter 방법 으로 Adapter 를 설정 합 니 다.

 @Override
  public void setAdapter(ListAdapter adapter) {
    //         adapter,      Adapter      
    if (mAdapter != null && mDataSetObserver != null) {
      mAdapter.unregisterDataSetObserver(mDataSetObserver);
    }

    //     

    super.setAdapter(adapter);

    if (mAdapter != null) {
      mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();
      mOldItemCount = mItemCount;
      //        
      mItemCount = mAdapter.getCount();
      checkFocus();
      //      :             
      mDataSetObserver = new AdapterDataSetObserver();
      //          Adapter ,       DataSetObservable 
      mAdapter.registerDataSetObserver(mDataSetObserver);

      //     
    } else {
      //     
    }

    requestLayout();
  }

Adapter 를 설정 할 때 Adapter DataSetObserver 를 구축 하고 마지막 으로 이 관찰 자 를 adapter 에 등록 합 니 다.그러면 우리 의 피 관찰자,관찰자 가 모두 있 습 니 다.
AdapterDataSetObserver 는 ListView 의 부모 클래스 AbsListView 에 정 의 됩 니 다.코드 는 다음 과 같 습 니 다.

 class AdapterDataSetObserver extends AdapterView<ListAdapter>.AdapterDataSetObserver {
    @Override
    public void onChanged() {
      super.onChanged();
      if (mFastScroll != null) {
        mFastScroll.onSectionsChanged();
      }
    }

    @Override
    public void onInvalidated() {
      super.onInvalidated();
      if (mFastScroll != null) {
        mFastScroll.onSectionsChanged();
      }
    }
  }

AbsListView 의 부모 클래스 AdapterView 를 계승 한 AdapterDataSetObserver 입 니 다.코드 는 다음 과 같 습 니 다.

class AdapterDataSetObserver extends DataSetObserver {

    private Parcelable mInstanceState = null;
    //   Adapter notifyDataSetChanged            onChanged  ,        
    @Override
    public void onChanged() {
      mDataChanged = true;
      mOldItemCount = mItemCount;
      //   Adapter      
      mItemCount = getAdapter().getCount();

      // Detect the case where a cursor that was previously invalidated has
      // been repopulated with new data.
      if (AdapterView.this.getAdapter().hasStableIds() && mInstanceState != null
          && mOldItemCount == 0 && mItemCount > 0) {
        AdapterView.this.onRestoreInstanceState(mInstanceState);
        mInstanceState = null;
      } else {
        rememberSyncState();
      }
      checkFocus();
      //     ListView、GridView AdapterView  
      requestLayout();
    }

    //     

    public void clearSavedState() {
      mInstanceState = null;
    }
  }
ListView 의 데이터 가 변 할 때 Adapter 의 notifyDataSetChanged 함 수 를 호출 합 니 다.이 함 수 는 DataSetObservable 의 notifyChanged 함 수 를 호출 합 니 다.이 함 수 는 모든 관찰자(AdapterDataSetObserver)의 onChanged 방법 을 호출 합 니 다.이것 이 바로 관찰자 모드 다!
요약:AdapterView 에는 내부 클래스 AdapterDataSetObserver 가 있 습 니 다.ListView 에서 Adapter 를 설정 할 때 AdapterDataSetObserver 를 구축 하고 Adapter 에 등록 합 니 다.이것 이 바로 관찰자 입 니 다.한편,Adapter 에는 관찰 가능 한 데이터 세트 DataSetObservable 이 포함 되 어 있 습 니 다.데이터 수량 이 변경 되 었 을 때 개발 자 는 AdapternotifyDataSetChanged 를 수 동 으로 호출 하고 notifyDataSetChanged 는 실제 적 으로 DataSetObservable 의 notify Changed 함 수 를 호출 합 니 다.이 함 수 는 모든 관찰자 의 onChanged 함 수 를 옮 겨 다 닙 니 다.AdapterDataSetObserver 의 onChanged 함수 에서 Adapter 의 데이터 세트 의 새로운 수량 을 가 져 온 다음 ListView 의 requestLayout()방법 으로 레이아웃 을 다시 하고 사용자 인터페이스 를 업데이트 합 니 다.
비교적 유명한 사용 관찰자 모델 의 오픈 소스 프레임 워 크 는
  • EventBus
  • AndroidEventBus
  • otto
  • 패턴 총화
    장점.
    관찰자 모델 은 주제 와 구체 적 인 관찰자 의 결합 을 해제 하고 결합 하 는 쌍방 이 구체 적 인 것 에 의존 하 는 것 이 아니 라 추상 에 의존 하도록 한다.그래서 각자 의 변 화 는 다른 쪽 의 변화 에 영향 을 주지 않 는 다.
    결점.
    의존 관 계 는 완전히 해제 되 지 않 았 고 추상 적 인 통지 자 는 추상 적 인 관찰자 에 게 의존 했다.
    적용 필드
    한 대상 의 변화 가 다른 대상 으로 바 뀌 어야 할 때,구체 적 으로 몇 개의 대상 이 바 뀌 어야 할 지 모른다.
    하나의 추상 적 인 특정한 유형 은 두 가지 측면 이 있 는데 그 중의 한 가지 측면 이 다른 측면 에 의존 할 때 관찰자 모델 로 이 두 가 지 를 독립 된 대상 에 포장 하여 각자 독립 적 으로 변화 시 키 고 재 활용 할 수 있다.
    이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.

    좋은 웹페이지 즐겨찾기