聊聊mvp架构

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

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

@Override
public 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);
}

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

@Override
public 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
 */




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值