淺學設計模式之觀察者<Observer>模式及在android中的應用
轉自https://www.eoeandroid.com/home.php?mod=space&uid=871316&do=blog&id=48239
最近在學習下設計模式,而加深學習的不錯的方法就是把心得寫出來吧。記錄下自己的理解。現在自己看的書是《head.Frist設計模式》這本書。比較不錯,想學習設計模式的朋友可以看下這本書。
觀察者<Observer>模式(有時又被稱為發布-訂閱<Publish/Subscribe>模式、模型-視圖<Model/View>模式、源-收聽者<Source/Listener>模式或從屬者<Dependents>模式)是軟件設計模式的一種。在此種模式中,一個目標物件管理所有相依於它的觀察者物件,並且在它本身的狀態改變時主動發出通知。這通常透過唿叫各觀察者所提供的方法來實現。此種模式通常被用來實作事件處理係統。(源自百度百科)
(五月三十一號修正內容)
看下結構圖:
開始一個小例子
Observer接口:
- public interface ISubscribe {
- void getNewPaper();
- }
實現Observer接口的觀察者:
個人訂閱者:
- public class PersonalSubscriber implements ISubscribe {
- private String strName;
- public void setNewsPaperName(String strName){
- this.strName = strName;
- }
- public String getNewsPaperName(){
- return strName;
- }
- @Override
- public void getNewPaper() {
- // TODO Auto-generated method stub
- System.out.println("我是個人用戶,我得到了我的報紙:"+getNewsPaperName());
- }
- }
企業訂閱者:
- public class EnterpriseSubscriber implements ISubscribe {
- private String strName;
- public void setNewsPaperName(String strName){
- this.strName = strName;
- }
- public String getNewsPaperName(){
- return strName;
- }
- @Override
- public void getNewPaper() {
- // TODO Auto-generated method stub
- System.out.println("我是企業用戶,我得到了我的報紙:"+getNewsPaperName());
- }
- }
被觀察者:Subject
- public abstract class Publish {
- public List<ISubscribe> list;
- public Publish(){
- list = new ArrayList();
- }
- public void registered(ISubscribe iSubscribe){
- list.add(iSubscribe);
- }
- public void unregistered(ISubscribe iSubscribe){
- list.remove(iSubscribe);
- }
- public abstract void sendNewsPaper();
- }
這裏使用的是抽象類
下麵是實現:
- public class PostOffice extends Publish{
- @Override
- public void sendNewsPaper() {
- // TODO Auto-generated method stub
- Iterator iterator = list.iterator();
- while(iterator.hasNext()){
- ((ISubscribe) iterator.next()).getNewPaper();
- }
- }
- }
測試:
- public static void main(String[] args) {
- // TODO Auto-generated method stub
- PostOffice postOffice = new PostOffice();
- //得到個人用戶
- PersonalSubscriber person = new PersonalSubscriber();
- person.setNewsPaperName("《南方周末》");
- //得到企業用戶
- EnterpriseSubscriber enterprise = new EnterpriseSubscriber();
- enterprise.setNewsPaperName("《企業報》");
- //注冊觀察者
- postOffice.registered(person);
- postOffice.registered(enterprise);
- //發放報紙
- postOffice.sendNewsPaper();
- }
測試結果:
- 我是個人用戶,我得到了我的報紙:《南方周末》
- 我是企業用戶,我得到了我的報紙:《企業報》
下麵是有些錯誤五月18號版本:後麵有錯誤解析!
舉個例子,張三從郵局訂閱了《南方周末》,李四從郵局訂閱了《新京報》,王五從郵局裏麵訂閱了《南方都市報》。當報紙抵達郵局的時候,郵局就會把報紙送遞訂閱者。而不需要訂閱者天天到郵局詢問報紙是否到達郵局。
下麵開始代碼:首先應該是個訂閱者接口:
- package cn.demo;
- public interface ISubscribe {
- //從郵局訂閱報紙
- public void registered(PostOffice postOffice);
- //從郵局退訂報紙
- public void unregistered(PostOffice postOffice);
- //獲取報紙名稱
- public void getNewsPaper();
- }
第三個方法非必須,前兩個方法必須要有。(應修改為前兩個非必須,第三個方法必須有)
然後有三個訂閱者類:
張三:
- package cn.demo;
- public class ZhangSan implements ISubscribe{
- private String mName;
- private String mNewsPaperName;
- public ZhangSan(String mName, String mNewsPaperName) {
- this.mName = mName;
- this.mNewsPaperName = mNewsPaperName;
- }
- public String getName() {
- return mName;
- }
- public String getNewsPaperName() {
- return mNewsPaperName;
- }
- @Override
- public void registered(PostOffice postOffice){
- postOffice.registeredNewsPaper(this);
- }
- @Override
- public void unregistered(PostOffice postOffice){
- postOffice.unregisteredNewsPaper(this);
- }
- @Override
- public void getNewsPaper() {
- // TODO Auto-generated method stub
- System.out.println("我是"+getName()+",我收到我訂閱的"+getNewsPaperName());
- }
- }
李四:
- package cn.demo;
- public class Lisi implements ISubscribe{
- private String mName;
- private String mNewsPaperName;
- public Lisi(String mName, String mNewsPaperName) {
- this.mName = mName;
- this.mNewsPaperName = mNewsPaperName;
- }
- public String getName() {
- return mName;
- }
- public String getNewsPaperName() {
- return mNewsPaperName;
- }
- @Override
- public void unregistered(PostOffice postOffice){
- postOffice.unregisteredNewsPaper(this);
- }
- @Override
- public void registered(PostOffice postOffice){
- postOffice.registeredNewsPaper(this);
- }
- @Override
- public void getNewsPaper() {
- // TODO Auto-generated method stub
- System.out.println("我是"+getName()+",我收到我訂閱的"+getNewsPaperName());
- }
- }
王五:
- package cn.demo;
- public class Wangwu implements ISubscribe{
- private String mName;
- private String mNewsPaperName;
- public Wangwu(String mName, String mNewsPaperName) {
- this.mName = mName;
- this.mNewsPaperName = mNewsPaperName;
- }
- public String getName() {
- return mName;
- }
- public String getNewsPaperName() {
- return mNewsPaperName;
- }
- @Override
- public void registered(PostOffice postOffice){
- postOffice.registeredNewsPaper(this);
- }
- @Override
- public void unregistered(PostOffice postOffice){
- postOffice.unregisteredNewsPaper(this);
- }
- @Override
- public void getNewsPaper() {
- // TODO Auto-generated method stub
- System.out.println("我是"+getName()+",我收到我訂閱的"+getNewsPaperName());
- }
- }
三個訂閱者,都有方法訂閱報紙,或者取消訂閱。其實關於訂閱者的信息,是存儲在郵局類裏麵。
- import java.util.ArrayList;
- import java.util.List;
- public class PostOffice {
- private List<ISubscribe> SubscribeList = new ArrayList<ISubscribe>();
- public void registeredNewsPaper(ISubscribe subscribe) {
- SubscribeList.add(subscribe);
- }
- public void unregisteredNewsPaper(ISubscribe subscribe) {
- if (subscribe != null) {
- SubscribeList.remove(subscribe);
- }
- }
- public void getNewsPaper(boolean bool) {
- if (bool) {
- sendNewsPaper();
- }
- }
- public void sendNewsPaper() {
- for(ISubscribe subscribe : SubscribeList) {
- subscribe.getNewsPaper();
- }
- }
- }
測試類:
- public static void main(String[] args) {
- // TODO Auto-generated method stub
- PostOffice mPostOffice = new PostOffice();
- ISubscribe zhangsan = new ZhangSan("張三", "《南方周末》");
- ISubscribe lisi = new Lisi("李四", "《新京報》");
- ISubscribe wangwu = new Wangwu("王五", "《南方都市報》");
- //開始訂閱報紙
- zhangsan.registered(mPostOffice);
- lisi.registered(mPostOffice);
- wangwu.registered(mPostOffice);
- //郵局收到報紙,開始發放
- mPostOffice.getNewsPaper(true);
- //李四退訂
- lisi.unregistered(mPostOffice);
- //郵局收到報紙,開始發放
- mPostOffice.getNewsPaper(true);
- }
打印結果:
- 我是張三,我收到我訂閱的《南方周末》
- 我是李四,我收到我訂閱的《新京報》
- 我是王五,我收到我訂閱的《南方都市報》
- 我是張三,我收到我訂閱的《南方周末》
- 我是王五,我收到我訂閱的《南方都市報》
這裏隻是提供了一個簡單的例子,而且代碼你會發現有冗餘,三個訂閱者類,幾乎是一樣,因為沒有在類裏麵添加他們獨自的屬性,可以用一個Person類來替代。這裏寫這三個類是為了顯示清晰。
關於五月18號的例子,觀察者沒必要有注冊和取消注冊的方法。他們的方法的實現也是調用的被觀察者的注冊和取消注冊,不如直接使用被觀察者的方法。
在Android中,button.setOnclickListener()這個方式是比較常見的觀察者模式:當然,眾所周知,onClick是著名的回調方法,在這裏不會研究回調,不用太在意。
看下代碼:
- Button button1 = (Button)findViewById(R.id.button1);
- Button button2 = (Button)findViewById(R.id.button2);
- Button button3 = (Button)findViewById(R.id.button3);
- button1.setOnClickListener(this);
- button2.setOnClickListener(this);
- button3.setOnClickListener(this);
- }
- @Override
- public void onClick(View v) {
- // TODO Auto-generated method stub
- switch(v.getId()) {
- case R.id.button1 :
- // do some thing
- case R.id.button2 :
- // do some thing
- case R.id.button3 :
- // do some thing
- }
- }
先button.setOnclickListener()進行注冊。相當於郵局中的lisi.registered(mPostOffice),此處錯誤,應該是第一例中的postOffice.registered(person)。onclick響應事件相當於郵局中的
- public void sendNewsPaper() {
- for(ISubscribe subscribe : SubscribeList) {
- subscribe.getNewsPaper();
- }
- }
這個中的subscribe.getNewsPaper()這個方法。View.onClickListener相當於郵局。View也就是button是我們的訂閱者,也就相當於張三。我們也可以在getNewsPaper中加一些自己的switch判斷。
最後,如果我們的郵局要實現類似button這樣的回調這麼實現呢?
- ISubscribe isubscribe;
- @Override
- public void getNewsPaper() {
- // TODO Auto-generated method stub
- System.out.println("我是"+getName()+",我收到我訂閱的"+getNewsPaperName());
- isubscribe.getNewsPaper();
- }
最後更新:2017-04-03 08:26:14