观察者模式在MVP中的应用

来源:互联网 发布:linux系统ls命令 编辑:程序博客网 时间:2024/05/21 00:46

一、要实现的效果

  首先简单介绍一下我在项目中为什么需要使用观察者模式加MVP模式
  1、为什么使用MVP模式:下图是我所开发项目的一个主要功能界面(骑行界面),从图中我们可以看到这个界面有很多控件,比如温度、电量、当前速度、蓝牙、锁车、里程···在我重构之前所有的代码都放在了一个Activity里面,UI更新与逻辑实现全部混合在一起,有2300多行,每次需要改动时都很麻烦,查找很不方便。所以重构时决定采用MVP模式,将UI更新与逻辑实现进行解耦。
骑行界面
  2、为什么使用观察者模式:使用MVP模式之后很好地实现了更新UI与逻辑实现的解耦,本来已经可以了。但是后来又增加了一个新需求:增加一个简便操作界面,简便操作界面如下图所示:
简便操作界面
可以看到简便操作界面就是在前一个界面的基础上,UI风格完全改变,功能相对于前一个界面有所减少,但是也增加了一些新功能。简便操作界面跟骑行界面功能上很多都是重合的,因此很明显我们需要重用逻辑代码,因为我们使用了mvp模式,所以我们的UI跟逻辑本身已经实现了分离,现在关键是当逻辑处理完之后,我们需要更新UI,那么用哪种方式更新UI好呢,因为有2个界面的UI需要更新。这里我使用了观察者模式,为什么使用观察者模式?从观察者模式的定义你大概可以看出使用它的原因了:++“有时被称作发布/订阅模式,观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。”++
一个逻辑处理完了之后,比如获取温度,我们需要更新两个UI,这是一种一对多的关系,因此适合使用观察者模式。

二、MVP模式简要介绍

  MVP把Activity中的UI逻辑抽象成View接口,把业务逻辑抽象成Presenter接口,Model类还是原来的Model。
  MVP模式的作用 :
1、分离了视图逻辑和业务逻辑,降低了耦合
2、Activity只处理生命周期的任务,代码变得更加简洁
3、视图逻辑和业务逻辑分别抽象到了View和Presenter的接口中去,提高代码的可阅读性
4、Presenter被抽象成接口,可以有多种具体的实现,所以方便进行单元测试
5、把业务逻辑抽到Presenter中去,避免后台线程引用着Activity导致Activity的资源无法被系统回收从而引起内存泄露和OOM
6、UI接口中定义了更新UI的方法,presenter接口中定义了业务逻辑方法,我们从这两个接口文件中就可以一目了然地看到我们有哪些逻辑操作,有哪些UI更新方法,功能一目了然。
MVP模式类图

三、观察者模式简要介绍:

  1. 概述:
      有时被称作发布/订阅模式,观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
  2. 解决的问题:
      将一个系统分割成一个一些类相互协作的类有一个不好的副作用,那就是需要维护相关对象间的一致性。我们不希望为了维持一致性而使各类紧密耦合,这样会给维护、扩展和重用都带来不便。观察者就是解决这类的耦合关系的。
  3. 模式中的角色:
      3.1抽象主题(Subject):它把所有观察者对象的引用保存到一个聚集里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象。
      3.2具体主题(ConcreteSubject):将有关状态存入具体观察者对象;在具体主题内部状态改变时,给所有登记过的观察者发出通知。
      3.3 抽象观察者(Observer):为所有的具体观察者定义一个接口,在得到主题通知时更新自己。
      3.4具体观察者(ConcreteObserver):实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题状态协调。
  4. 模式解读:
      
    4.1 观察者模式的类图
    观察者模式类图

四、结合代码讲解观察者模式在MVP模式中的应用

为方便起见,我新建一个Java工程来模拟:ObseverMvp
项目文件结构如下:
==com.zha==
Client.java[类]
==com.zha.logic==
CommLogicPresenter.java[抽象类]
ConcreteCommLogic.java[类]
IRidePresenter.java[接口]
ISimpleOperatorPresenter.java[接口]
==com.zha.ui==
CommUIView.java[抽象类]
IRideView.java[接口]
ISimpleOperatorView.java[接口]
RideActivity.java[类]
SimpleOperatorActivity.java[类]
该项目的类图如下:
这里写图片描述

MVP:

  从上面的目录结构我们可以很清楚地可以看到三部分,Client.java部分、logic包下面的部分、ui包下面的部分,很好地对ui跟逻辑进行了分离。
  logic包下以Presenter结尾的三个文件是业务逻辑的抽象[MVP中的P]。为什么分成三个文件?首先CommLogicPresenter.java类中定义了两个界面共有的业务逻辑,可以很好地实现逻辑复用;IRidePresenter.java类定义了骑行页面有但是简便操作界面没有的业务逻辑;ISimpleOperatorPresenter.java类定义了简便操作界面有但是骑行页面没有的业务逻辑。从这三个类中我们可以很清楚的看到它们有哪些共同的业务逻辑,骑行界面有哪些业务逻辑,简便操作界面有哪些业务逻辑,功能一目了然。
  ui包下以View结尾的三个文件是视图逻辑的抽象[MVP中的V]。为什么分成三个文件?首先CommUIView.java类中定义了两个界面共有的视图逻辑,好处是我们调用的时候可以使用同一段代码,但是不同的界面又可以对同一个接口有不同的实现,例如更新温度,看下面代码:

@Overridepublic void noticyRefreshTemperature() {    System.out.println("事件---获取温度");    for (CommUIView commUIView : commUIViews) {        commUIView.refreshUI_temperature();    }}

IRideView.java定义了骑行界面有但是简便操作界面没有的视图逻辑,ISimpleOperatorView.java定义了简便操作界面有但是骑行界面没有的视图逻辑。

观察者模式

  阅读上面我们知道观察者模式有四个角色,抽象主题、具体主题、抽象观察者、具体观察者。先来看抽象主题,CommLogicPresenter.java,它里面定义了添加观察者和删除观察者的接口:

/** * 添加观察者 * @param commUIView */public abstract void attach(CommUIView commUIView);/** * 移除观察者 * @param commUIView */public abstract void detach(CommUIView commUIView);

  然后再来看具体主题ConcreteCommLogic.java,1、首先它实现了抽象主题,2、然后它的另一个作用是当业务逻辑有改变时,通知所有的UI进行视图更新,还是上面一段代码:

@Overridepublic void noticyRefreshTemperature() {    System.out.println("事件---获取温度");    for (CommUIView commUIView : commUIViews) {        commUIView.refreshUI_temperature();    }}

  从上面代码可以看到当我们获取到温度后,我们需要循环调用commUIView.refreshUI_temperature();方法来通知所有登记过(添加)的观察者来更新视图。
  接着是抽象观察者,CommUIView.java,它为所有的具体观察者定义一个接口,在得到主题通知时更新自己。
  最后是具体观察者,RideActivity.java和SimpleOperatorActivity.java,它们实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题状态协调,他们对同一个主题的通知可以有不同的实现方式。

下面我贴出所有的源码:

CommLogicPresenter它既是一个抽象的主题(抽象角色),1、他定义了添加和删除观察者的抽象方法;2、同时它又是一个抽象的业务逻辑(Presenter)。他定义了两个界面共有的业务逻辑方法。

package com.zha.logic;import com.zha.ui.CommUIView;public abstract class CommLogicPresenter {    /**     * 添加观察者     * @param commUIView     */    public abstract void attach(CommUIView commUIView);    /**     * 移除观察者     * @param commUIView     */    public abstract void detach(CommUIView commUIView);    /**     * 刷新温度     */    public abstract void noticyRefreshTemperature();    /**     * 启动定时器     */    public abstract void startTimer();    /**     * 停止定时器     */    public abstract void stopTimer();}

ConcreteCommLogic 它是一个具体的观察者,也实现了具体的业务逻辑

package com.zha.logic;import java.util.ArrayList;import java.util.List;import java.util.concurrent.Executors;import java.util.concurrent.ScheduledExecutorService;import java.util.concurrent.TimeUnit;import com.zha.ui.CommUIView;import com.zha.ui.IRideView;import com.zha.ui.ISimpleOperatorView;public class ConcreteCommLogic extends CommLogicPresenter implements IRidePresenter,ISimpleOperatorPresenter{    /**     * 观察者列表     */    private List<CommUIView> commUIViews= new ArrayList<CommUIView>();    private ScheduledExecutorService service = Executors              .newSingleThreadScheduledExecutor();      private Runnable runnable = new Runnable() {          public void run() {              noticyRefreshTemperature();        }      };     @Override    public void attach(CommUIView commUIView) {        commUIViews.add(commUIView);    }    @Override    public void detach(CommUIView commUIView) {        commUIViews.remove(commUIView);    }    @Override    public void noticyRefreshTemperature() {        System.out.println("事件---获取温度");        for (CommUIView commUIView : commUIViews) {            commUIView.refreshUI_temperature();        }    }    @Override    public void clickLock() {        System.out.println("事件---锁定");        for (CommUIView commUI : commUIViews) {            if (commUI instanceof IRideView) {                ((IRideView)commUI).refreshUI_lockStatus();            }        }    }    @Override    public void clickChangeAssistantMode() {        System.out.println("事件---切换助力模式");        for (CommUIView commUI : commUIViews) {            if (commUI instanceof ISimpleOperatorView) {                ((ISimpleOperatorView)commUI).changeAssistantMode();            }        }    }    @Override    public void startTimer() {        System.out.println("事件---启动定时器");        // 第二个参数为首次执行的延时时间,第三个参数为定时执行的间隔时间          service.scheduleAtFixedRate(runnable, 10, 8, TimeUnit.SECONDS);                 }    @Override    public void stopTimer() {    }}

IRidePresenter定义了骑行界面独有的业务逻辑

package com.zha.logic;public interface IRidePresenter {    /**     * 锁定车辆     */    void clickLock();}

ISimpleOperatorPresenter定义了简便操作界面独有的逻辑

package com.zha.logic;public interface ISimpleOperatorPresenter {    /**     * 切换助力模式     */    void clickChangeAssistantMode();}

CommUIView它既是一个抽象的主题;又是一个抽象的视图逻辑(View)。他定义了界面所需要的视图逻辑方法。

package com.zha.ui;public abstract class CommUIView {      /**     * 更新温度     */    public abstract void refreshUI_temperature();}

IRideView定义了骑行界面独有的视图逻辑

package com.zha.ui;public interface IRideView {    /**     * 更新锁定状态     */    void refreshUI_lockStatus();}

定义了简便操作界面独有的视图逻辑

package com.zha.ui;public interface ISimpleOperatorView {    /**     * 切换助力模式     */    void changeAssistantMode();}

RideActivity它是具体的观察者,同时也是具体的视图逻辑

package com.zha.ui;public class RideActivity extends CommUIView implements IRideView{    @Override    public void refreshUI_temperature() {        System.out.println("更新UI---骑行*更新温度---");    }    @Override    public void refreshUI_lockStatus() {        System.out.println("更新UI---骑行*更新锁定---");    }}

SimpleOperatorActivity它是具体的观察者,同时也是具体的视图逻辑

package com.zha.ui;public class SimpleOperatorActivity extends CommUIView implements ISimpleOperatorView{    @Override    public void refreshUI_temperature() {        System.out.println("更新UI---简便操作*更新温度---");    }    @Override    public void changeAssistantMode() {        System.out.println("更新UI---简便操作*切换助力模式---");    }}

最后是Client

package com.zha;import com.zha.logic.ConcreteCommLogic;import com.zha.ui.RideActivity;import com.zha.ui.SimpleOperatorActivity;public class Client {    public static void main(String[] args) {        ConcreteCommLogic concreteCommLogic = new ConcreteCommLogic();        RideActivity rideFragment = new RideActivity();        SimpleOperatorActivity simpleOperatorActivity = new SimpleOperatorActivity();        concreteCommLogic.attach(rideFragment);        concreteCommLogic.attach(simpleOperatorActivity);        concreteCommLogic.noticyRefreshTemperature();        concreteCommLogic.clickLock();        concreteCommLogic.clickChangeAssistantMode();        concreteCommLogic.startTimer();    }}

运行结果:
事件—获取温度
更新UI—骑行_更新温度
更新UI—简便操作_更新温度
事件—锁定
更新UI—骑行_更新锁定
事件—切换助力模式
更新UI—简便操作_切换助力模式
事件—启动定时器
事件—获取温度
更新UI—骑行_更新温度
更新UI—简便操作_更新温度
事件—获取温度
更新UI—骑行_更新温度
更新UI—简便操作_更新温度
源码下载地址:源码下载地址

1 0