English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Androidのプログラミングデザインパターンの観察者パターンの例詳細

この記事では、Androidプログラミングデザインパターンの観察者パターンの例を紹介します。皆さんに参考にしていただくために、以下の通りです:

1. 介绍

観察者パターンは非常に使用されるパターンで、最も一般的にはGUIシステム、購読-発行システムで使用されます。このパターンの重要な役割は解耦であり、観察対象と観察者を解耦し、彼らの依存関係を最小限に抑え、依存関係がなくなることもあります。GUIシステムの場合、アプリケーションのUIは変動が大きく、特にビジネスの変更や製品の要件の変更に伴って、アプリケーションインターフェースも頻繁に変更されますが、ビジネスロジックの基本的な変更はあまりありません。この場合、GUIシステムはこのような状況に対応するためのメカニズムを必要とし、UIレイヤーと具体的なビジネスロジックを解耦する必要があります。観察者パターンがこの場合に役立ちます。

2. 定義

オブジェクト間に一対多の依存関係を定義し、あるオブジェクトが状態を変更すると、その依存するすべてのオブジェクトが通知を受け取り自動的に更新されるようにします。

3. 使用シーン

関連行動シーン、注意すべきことは、関連行動は分割可能であり、「組み合わせ」関係ではなくです。

イベントの多段階トリガシーン。

システム間のメッセージ交換シーン、例えばメッセージキュー、イベントバスの処理メカニズム。

4. 観察者パターンのUMLクラス図

UMLクラス図:

役割紹介:

サブジェクト:抽象的なテーマ、つまり観察対象(観察可能)の役割です。抽象的なテーマの役割は、すべての観察者オブジェクトの参照を集約に保存し、各テーマは任意の数の観察者を持つことができます。抽象的なテーマは、観察者オブジェクトを追加および削除するためのインターフェースを提供します。

ConcreteSubject:具体的なトピック、この役割は状態を具体的な観察者オブジェクトに保存し、具体的なトピックの内部状態が変更されたときにすべての登録された観察者に通知を発行します。具体的なトピック役割は、具体的な被観察者(ConcreteObservable)役割と呼ばれます。

Observer:抽象的な観察者、この役割は観察者の抽象クラスであり、トピックの変更通知を受け取ったときに自身を更新するためのインターフェースを定義します。

ConcreteObserver:具体的な観察者、この役割は抽象的な観察者役割が定義する更新インターフェースを実装し、トピックの状態が変更されたときに自身の状態を更新します。

五、簡単な実装

ここでは、ドラマを追いかける例を挙げます。通常、最新のドラマを逃さないように、そのドラマに登録したり注目したりします。ドラマが更新されたら、最初に通知されます。以下に簡単に実装します。

抽象观察者类:

/**
 * 抽象观察者类,为所有具体观察者定义一个接口,在得到通知时更新自己
 */
public interface Observer {
  /**
   * 有更新
   * 
   * @param message 消息
   */
  public void update(String message);
}

抽象被观察者类:

/**
 * 抽象被观察者类
 */
public interface Observable {
  /**
   * 推送消息
   * 
   * @param message 内容
   */
  void push(String message);
  /**
   * サブスクライブ
   * 
   * @param observer 订阅者
   */
  void register(Observer observer);
}

具体的观察者类:

/**
 * 具体的观察者类,也就是订阅者
 */
public class User implements Observer {
  @Override
  public void update(String message) {
    System.out.println(name + ", + message + "更新了!");
  }
  // 订阅者的名字
  private String name;
  public User(String name) {
    this.name = name;
  }
}

具体的被观察者类:

/**
 * 具体的被观察者类,也就是订阅的节目
 */
public class Teleplay implements Observable{
  private List<Observer> list = new ArrayList<Observer>();//サブスクライバーを保存
  @Override
  public void push(String message) {
    for(Observer observer:list){
      observer.update(message);
    }
  }
  @Override
  public void register(Observer observer) {
    list.add(observer);
  }
}

実現:

public class Client {
  public static void main(String[] args) {
    //オブザーバブル、ここはユーザーがサブスクライブしたドラマです
    Teleplay teleplay = new Teleplay();
    //オブザーバー、ここはサブスクライバーです
    User user1 = new User("小明");
    User user2 = new User("小光");
    User user3 = new User("小蘭");
    //サブスクライブ
    teleplay.register(user1);
    teleplay.register(user2);
    teleplay.register(user3);
    //新しいメッセージを推送
    teleplay.push("xxxドラマ");
  }
}

結果:

小明、xxxドラマが更新されました!
小光、xxxドラマが更新されました!
小蘭、xxxドラマが更新されました!

上記のコードから、一対多のメッセージ推送が実現されていることがわかります。推送されるメッセージはObserverとObservableなどの抽象クラスに依存しており、UserとTeleplayは全くの関係がありません。これにより、サブスクライブシステムの柔軟性と拡張性が保証されています。

六、Androidソースコードのオブザーバーモード

1、BaseAdapter

BaseAdapterは皆さんも馴染みがあるでしょう。ListViewのアダプターではこのクラスを継承しています。以下に簡単に分析します。

BaseAdapterの部分コード:

public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {
  //データセットオブザーバー
  private final DataSetObservable mDataSetObservable = new DataSetObservable();
  public boolean hasStableIds() {
    return false;
  }
  public void registerDataSetObserver(DataSetObserver observer) {
    mDataSetObservable.registerObserver(observer);
  }
  public void unregisterDataSetObserver(DataSetObserver observer) {
    mDataSetObservable.unregisterObserver(observer);
  }
  /**
   * 当数据集变化时,通知所有观察者
   */
  public void notifyDataSetChanged() {
    mDataSetObservable.notifyChanged();
  }
}

看看mDataSetObservable.notifyChanged()方法:

public class DataSetObservable extends Observable<DataSetObserver> {
  /**
   * 对每个观察者调用 {@link DataSetObserver#onChanged}。
   * 当数据集的内容发生变化时调用。接收者
   * 下次查询数据集时将获取新内容。
   */
  public void notifyChanged() {
    synchronized(mObservers) {
      // 由于onChanged()由应用实现,它可以执行任何操作,包括
      // 从 {@link mObservers} 中移除自身 - 如果
      // 在ArrayList {@link mObservers} 上使用迭代器。
      // 为了避免此类问题,只需按反向顺序遍历列表。
      for (int i = mObservers.size() - 1; i >= 0; i--) {
        mObservers.get(i).onChanged();
      }
    }
  }
}

mDataSetObservable.notifyChanged()の中で、すべてのオブザーバーを巡回し、onChanged()を呼び出して、オブザーバーに何が起こったかを知らせることができます。

それでは、オブザーバーはどのようにして来たのでしょうか。それはsetAdapterメソッドです。以下のコードを参照してください:

@Override
public void setAdapter(ListAdapter adapter) {
    if (mAdapter != null && mDataSetObserver != null) {
      mAdapter.unregisterDataSetObserver(mDataSetObserver);
    }
    resetList();
    mRecycler.clear();
    if (mHeaderViewInfos.size() > 0 || mFooterViewInfos.size() > 0) {
      mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, adapter);
    } else {
      mAdapter = adapter;
    }
    mOldSelectedPosition = INVALID_POSITION;
    mOldSelectedRowId = INVALID_ROW_ID;
    // AbsListView#setAdapterは選択モードの状態を更新します。
    super.setAdapter(adapter);
    if (mAdapter != null) {
      mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();
      mOldItemCount = mItemCount;
      mItemCount = mAdapter.getCount();
      checkFocus();
      mDataSetObserver = new AdapterDataSetObserver();
      mAdapter.registerDataSetObserver(mDataSetObserver);//オブザーバーを登録する
      ......省略
    }
}

AdapterDataSetObserverはListViewの親クラスであるAbsListViewで定義されており、データセットオブザーバーです。コードは以下の通りです:

class AdapterDataSetObserver extends AdapterView<ListAdapter>.AdapterDataSetObserver {
  @Override
  public void onChanged() {
    super.onChanged();
    if (mFastScroller != null) {
      mFastScroller.onSectionsChanged();
    }
  }
  @Override
  public void onInvalidated() {
    super.onInvalidated();
    if (mFastScroller != null) {
      mFastScroller.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();
    // 無効化された前のカーソルが検出された場合を検出します。
    // 新しいデータで再Populatedされています。
    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メソッドを呼び出します。これが観察者パターンです!

7、まとめ

利点:

オブザーバーと観察対象間は抽象的な結合であり、ビジネスの変化に対応します。

システムの柔軟性と拡張性を強化します。

欠点:

オブザーバーモードを使用する際には、開発効率と実行効率の問題を考慮する必要があります。プログラムには、オブザーバー、観察対象、開発、デバッグなどが含まれており、複雑です。Javaでは、メッセージの通知は一般的に順序実行されるため、観察者が遅延すると全体の実行効率に影響を与えます。この場合、通常、アシスト実装が採用されます。

Androidに関する詳細な内容に興味を持つ読者は、当サイトの特集を確認してください:《Android開発入門と進階ガイド》、《Androidデバッグ技術とよくある問題の解決方法集》、《Android基本コンポーネントの使用法要約》、《AndroidビューViewの技術要約》、《Androidレイアウトlayoutの技術要約》および《Androidコントロールの使用法要約》

このガイドが皆様のAndroidプログラム設計に役立つことを願っています。

声明:本文の内容はインターネットから取得しており、著作権者に帰属します。インターネットユーザーにより自発的に提供された内容であり、当サイトは所有権を持ちません。人工編集は行われていません。著作権に関する問題があれば、notice#wまでメールをお送りください。3codebox.com(メールを送信する際、#を@に変更してください)で通報し、関連する証拠を提供してください。一旦確認がとりたいとされると、当サイトは即座に侵害される可能性のあるコンテンツを削除します。

おすすめ