聊聊mvp架构

来源:互联网 发布:农村淘宝服务电话 编辑:程序博客网 时间:2024/05/21 23:34

Android中传统的没使用架构的,或者使用mvc结构的,activity都承载了太多功能,既当了view层又当了control层

关于mvc本文不多讨论了,记录下工作中的使用mvp结构的例子

mvp结构从下上实际为m->p-->v,即数据,控制,ui

先说下应用业务场景:

有关于门店shop的业务,有关于订单order的业务

public class Shop{   private String     id;   private String     icon;   private String     name;   private String     address;   private List<Time> runningTimes;   private int        bDRunning;   private int        mTRunning;   private int        eleRunning;   private int        wxRunning;   private int        wmOpen;   public int getWxRunning()   {      return wxRunning;   }   public void setWxRunning(int wxRunning)   {      this.wxRunning = wxRunning;   }   public String getId()   {      return id;   }   public void setId(String id)   {      this.id = id;   }   public String getIcon()   {      return icon;   }   public void setIcon(String icon)   {      this.icon = icon;   }   public String getName()   {      return name;   }   public void setName(String name)   {      this.name = name;   }   public String getAddress()   {      return address;   }   public void setAddress(String address)   {      this.address = address;   }   public List<Time> getRunningTimes()   {      return runningTimes;   }   public void setRunningTimes(List<Time> runningTimes)   {      this.runningTimes = runningTimes;   }   public int getbDRunning()   {      return bDRunning;   }   public void setbDRunning(int bDRunning)   {      this.bDRunning = bDRunning;   }   public int getmTRunning()   {      return mTRunning;   }   public void setmTRunning(int mTRunning)   {      this.mTRunning = mTRunning;   }   public int getEleRunning()   {      return eleRunning;   }   public void setEleRunning(int eleRunning)   {      this.eleRunning = eleRunning;   }   public int getTRunning()   {      return mTRunning;   }   public void setTRunning(int TRunning)   {      mTRunning = TRunning;   }   public int getWmOpen()   {      return wmOpen;   }   public void setWmOpen(int wmOpen)   {      this.wmOpen = wmOpen;   }   @Override   public String toString()   {      return "Shop{" +            "id='" + id + '\'' +            ", icon='" + icon + '\'' +            ", name='" + name + '\'' +            ", address='" + address + '\'' +            ", runningTimes=" + runningTimes +            ", bDRunning=" + bDRunning +            ", mTRunning=" + mTRunning +            ", eleRunning=" + eleRunning +            ", wmOpen=" + wmOpen +            '}';   }}
门店无非就是id,门店名,门店图片,地址等等,只关注最简单基本的数据就可以了

order也是id,支付时间,支付的钱,以及订单的各个状态(已支付,未支付,已确认,已关闭)

既然有两大业务线,这里以门店为例来说明,门店的业务需求接口有(只关注与服务器交互有关的):

1 获取门店数据

2 改变门店营业状态

3 设置店铺营业时间

4 获取门店日结账单

5 等等

以下提供的代码看起来多的话,只聚焦一个业务需求,获取门店的日结账单,将会以这个小业务需求为例子

显然这是M层,我们可以定义一个接口IShopModel来代表门店这个业务需求

public interface IShopModel{   interface OnGetShopLisener   {      void onSuccess(Shop shop);   }   interface ShopTimeGettingListener   {      void onGetTime(List<Time> times);   }   interface ShopTimeSettingListener   {      void onSetting(boolean b);   }   interface ChangeRunningStatusListener   {      void onSuccess();      void onFailed();   }   interface DayBillCallback   {      void onGetDayBill(DayBillResponse response);   }   interface NoticePubListener   {      void onPubSuccess();      void onPubFailed();   }   interface NoticeGettingListener   {      void onGettingNotice(String notice);   }   /**    * 获取门店数据    *    * @param context    * @param listener    */   VPayUIRequestV2 getShop(Context context,boolean isShow,OnGetShopLisener listener);   /**    * 改变门店营业状态    *    * @param context    * @param platform    * @param actionType    * @param listener    */   VPayUIRequestV2 changeRunningStatus(Context context, int platform, int actionType,                               ChangeRunningStatusListener listener);   /**    * 获取店铺营业时间    *    * @param context    * @param listener    */   VPayUIRequestV2 getShopRunningTime(Context context, ShopTimeGettingListener listener);   /**    * 设置店铺营业时间    *    * @param context    * @param listener    */   VPayUIRequestV2 setShopRunningTime(Context context, List<Time> mShopTimeList,                              ShopTimeSettingListener listener);   /**    * 获取门店日结账单    *    * @param context    * @param date    * @param callback    */   VPayUIRequestV2 getShopDayBill(Context context, long date, DayBillCallback callback);   /**    * 发布公告,由于该功能已屏蔽,暂不修改了    *    * @param context    * @param notice    * @param listener    */   void publishNOtice(Context context, String notice, NoticePubListener listener);   /**    * 获取公告    *    * @param context    * @param listener    */   void getNotice(Context context, NoticeGettingListener listener);}

然后我们再定义一个门店的实现类ShopModleImpl implements IshopModel

该类会对所有的业务进行真正的实现

@Overridepublic VPayUIRequestV2 getShopDayBill(final Context context, long date, final DayBillCallback callback){   DayBillRequest body = new DayBillRequest();   body.setDate(date);   String url ="";   final VRequest<?> requestV2 = new VRequest<DayBillResponse>(url, body, context, false)   {      @Override      public boolean onResponse(DayBillResponse response)      {         if (response.getCode() == HttpUtils.CODE_SUCCESS)         {            callback.onGetDayBill(response);            return true;         }         else         {            Toast.makeText(context, response.getReason(), Toast.LENGTH_SHORT).show();            return false;         }      }   };   requestV2.setShouldCache(false);   requestV2.send();   return requestV2;}

上面是对获取门店日结账单的实现例子

上面有一行代码

 callback.onGetDayBill(response);

这是对从服务器获取数据对象后的回调,按照以往后者mvc结构,一般会回调到Activity中,但在mvc中,我们不直接给activity,而是给presenter层

同理,定义一个代表门店业务的Presenter

/** * 关于门店业务的总接口 * 为减少接口爆发,就实现IShopPresenter这一个总接口,只复写与其业务有关的方法 */public interface IShopPresenter{   /**    * 获取门店数据    */   VPayUIRequestV2 getShop(Context context,boolean isShow);   /**    * 展示更改营业状态的dialog    *    * @param platform,要更改的平台,例如,0 美团,1百度,2饿了么    *    * 注意,这是错误的mvp使用,与model无关的控制不必交给presenter    * 否则会导致类似V->P->V->P->V这种无用功    */   // void showDialogChangeStatus(int platform);   /**    * 改变某个平台的营业状态    *    * @param platform   平台    * @param actionType 0 关闭,1 营业    */   VPayUIRequestV2 changeRunningStatus(Context context, int platform, int actionType);   /**    * 获取店铺营业时间    *    * @param context    */   VPayUIRequestV2 getShopRunningTime(Context context);   /**    * 设置店铺营业时间    *    * @param context    */   VPayUIRequestV2 setShopRunningTime(Context context, List<Time> mShopTimeList);   VPayUIRequestV2 getShopDayBill(Context context, long date);}

对于上面的接口,如果我们每个具体的presenter(例如获取门店数据presenter,设置营业时间presenter,获取门店日结账单presenter)直接实现它,会每个present中有大量的空实现方法,因此这里采用适配器模式,用一个基类BaseShopPresenter对其空实现,子类集成基类,复写与自身相关的业务方法

public class BaseShopPresenter implements IShopPresenter{   @Override   public VPayUIRequestV2 getShop(Context context,boolean isShow)   {      return null;   }   @Override   public VPayUIRequestV2 changeRunningStatus(Context context, int platform, int actionType)   {      return null;   }   @Override   public VPayUIRequestV2 getShopRunningTime(Context context)   {      return null;   }   @Override   public VPayUIRequestV2 setShopRunningTime(Context context, List<Time> mShopTimeList)   {      return null;   }   @Override   public VPayUIRequestV2 getShopDayBill(Context context, long date)   {      return null;   }}

这里ShopDayDillPresenter只复写与其相关的业务getShopDayBill()

public class ShopDayBillPresenter extends BaseShopPresenter{   public interface IShopDayBillView   {      void fillData(DayBillResponse response);   }   private IShopDayBillView mShopDayBillView;   private IShopModel       mShopModel;   public ShopDayBillPresenter(IShopDayBillView shopDayBillView)   {      mShopDayBillView = shopDayBillView;      mShopModel = new ShopModleImpl();   }   @Override   public VPayUIRequestV2 getShopDayBill(Context context, long date)   {      return mShopModel.getShopDayBill(context, date, new IShopModel.DayBillCallback()      {         @Override         public void onGetDayBill(DayBillResponse response)         {            mShopDayBillView.fillData(response);         }      });   }}

关键一行的代码

 mShopDayBillView.fillData(response);

可以看到由p控制view,填充数据

然后再看view层,怎么进行调用

在DayBillActivity中,我们只需在需要数据的时候

/** * 获取数据 * * @param date 指定获取时间 */public void initData(long date){   IShopPresenter iShopPresenter = new ShopDayBillPresenter(this);   mRequestV2 = iShopPresenter.getShopDayBill(mContext, date);}

就可以了,当数据返回回来的时候,会自动进行填充

@Overridepublic void fillData(DayBillResponse response){  //you can bind your data to view}

这样从一个业务请求开始,数据返回,数据填充,用mvp就表示完了.

在写mvp时,本人也看了网上很多其他的写法

最终用这个结尾吧

/** * 本项目于2016.04.15mvc改为mvp结构 * 在该结构中,modle层与mvc时一样保持不变,增加presenter层,其负责具体的数据业务控制逻辑, * 注意,只与数据有关的才交给其处理 * view层每一个具体的元素只负责其ui的初始化initView(Paramter pra...), * 数据的抽象初始化initData(Parameter pra...),以及需要model层帮助的交互 * mvp结构中的要注意的地方: * 交互事件不一定要传给Presenter * 这里有个原则,就是如果这个事件需要Model层的帮助, * 那事件必须传给Presenter * 否则请不要传给Presenter,让View自己处理, * 这样才不会导致类似V->P->V->P->V这种无用功 * 特别是网上抄来抄去的showDialoghideDialog * public interface ISplashView { * void showProcessBar(); * void hideProcessBar(); * void showNetError(); * void startNextActivity(); * } * 这个简单例子中可以看到View的接口方法就如此之多, * 按这种写法在实际工程中View的接口必定会膨胀开来。 * 一个接口中方法过多也必然违背了单一接口原则。 * 以后更换View实现的过程是非常痛苦的, * 或者说几乎更换不了View实现,那分层的意义就失去了。 * 之所以会导致这种问题是因为你的Presenter告诉View怎么去渲染, * 而不是告诉View“直接用什么去渲染。更通俗地说, * Presenter应该直接给View的加工后的数据。 * View自己负责要怎么去渲染。更多详细可以查看: * http://blog.csdn.net/duo2005duo/article/details/50594757 */




原创粉丝点击