最近比较忙,就很少更新自己的笔记(blog),昨天一个功能的开发中,总觉得那个页面会是一个很好的案例,就想把它记录到笔记中,今天忙中偷闲,把它单独拿出来做个笔记分享下。首先,我们先看下效果图:
看到这个的时候,当时瞬间有点兴奋,解决方案爆棚
- 在一个activity中用五个listview或者recyclerview控制显示和隐藏
- 在一个activity中用一个listview或者recyclerview
- tablayout+viewpager+fragment
- ………………
- ………………
或许还有更多比较好的解决方案,当我想到第三种方案的时候我就停下来了,决定选用第三种方案,如果选用第一种方案,五个控件控制显隐也是很繁琐很啰嗦的事情,总觉得有点奇葩,第二种方案,每次点击就要重新获取数据,会产生资源浪费,没必要每次都去数据请求,选择第三种方案,通过viewpager设置缓存页面数,只需要一次请求,然后不断切换fragment就可以实现以上效果,何乐而不为呢?
选择的解决方案先分析到这里,接下来便是功能实现的过程,实现功能的时候没做太多的考虑,首先写了一个AllOrderFragment.java,把全部订单的功能先实现了,但我看了下接口的时候,瞬间觉得自己太冲动了,除了接口中需要传入的“st”参数以外,其它参数都相同,请求的接口名称也相同,返回的json格式和字段名称也是一样的,我勒个擦,如果使用AllOrderFragment.java的方式,不断拷贝粘贴,功能上是ok的,但是重复造了五次轮子,确实有点坑爹,代码的冗余不需多说,赤裸裸的展示在我眼前。
想起了一句话,“很多事情的改变就因为一句我受够了”,是的,我受够了,前三年重复造的轮子太多,造成好多时候维护起来很麻烦,开发的过程中,不需要刻意去强加某种模式,当我们发现同样或者类似的代码写了两次或者两次以上的时候,就要想着去封装,或者用某种更好的方式去提高代码的可重用性,此刻,我决定把AllOrderFragment.java里面代码进行提取,如下:
/**
*
* created by zero on 2016-06-21
*
* 我的订单中,五个fragment的父类
*/
public abstract class BaseOrderFragment extends BaseFragment<MyOrderPresenter> implements IMyOrder
{
private RecyclerView rv_order;
private LinearLayout ll_no_order;
private int page_count;
private int load_count;
private int indexPager;
private List<MyOrderModel> model;
private LinearLayoutManager layoutManager;
private MyOrderAdapter adapter;
private RecyclerViewListener listener;
@Override
protected int initLayout() {
// TODO Auto-generated method stub
return R.layout.fragment_order_view;
}
@Override
protected void initData(View rootView) {
//从源码中剥离,此处未使用注解view
rv_order = (RecyclerView) rootView.findViewById(R.id.rv_order);
ll_no_order = (LinearLayout) rootView.findViewById(R.id.ll_no_order);
layoutManager = new LinearLayoutManager(getMyActivity());
layoutManager.setOrientation(LinearLayout.VERTICAL);
rv_order.setLayoutManager(layoutManager);
//子类通过requestParams()把st中不同的字段传过来
fragmentPresenter.requestOrder(requestParams());
}
//与子类沟通的关键之处
abstract protected String requestParams();
//以下三个方法是BaseFragment中的方法,既然是实际开发,就有点实际开发的样子,这部分代码未删去
@Override
protected Class<MyOrderPresenter> getPsClass() {
// TODO Auto-generated method stub
return MyOrderPresenter.class;
}
@Override
protected Object getIView() {
// TODO Auto-generated method stub
return this;
}
@Override
protected void clickView(View v) {
// TODO Auto-generated method stub
}
@Override
public void sendModel(MyOrderParentModel mModel, RequestParams params) {
//此处是对model的处理、recyclerview的上拉加载,代码已删
}
//以下代码可以无视,实际就是用来更新adapter
private void adapterNotify() {
// TODO Auto-generated method stub
if(adapter!=null){
if (indexPager > adapter.getItemCount() - 1)
{
MyOrderAdapter.isShowFooter = true;
} else
{
MyOrderAdapter.isShowFooter = false;
}
adapter.notifyDataSetChanged();
}
}
}
/**
*
* created by zero on 2016-06-21
*
*/
public class OrderListFragment extends BaseOrderFragment
{
@Override
protected String requestParams() {
// TODO Auto-generated method stub
return "list";
}
}
public class PendingPayFragment extends BaseOrderFragment
{
@Override
protected String requestParams() {
// TODO Auto-generated method stub
return "pending";
}
}
五个子类,此处只列举两个,其它三个代码几乎一样,只需要实现requestParams()就ok,以父类做为模板,一行代码搞定所有问题,昨天写完后,我瞬间笑了,第一次在fragment中写这么少的代码,^_^
/**
*
* created by zero on 2016-06-21
*
* 我的订单presenter
*
*/
public class MyOrderPresenter extends BasePresenter
{
private Context context;
private Handler handler;
private IMyOrder iView;
private final static int MY_ORDER_CODE = 0X000012;
/**
* 以下@Override的几个方法,都是父类BasePresenter的抽象方法,BasePresenter在此不暴露了
*/
@Override
public void initBaseData(Context context, Handler handler, Object iView,
Intent intent) {
this.context = context;
this.handler = handler;
if(iView instanceof IMyOrder){
this.iView = (IMyOrder) iView;
}
}
@Override
public void initData() {
// TODO Auto-generated method stub
}
/**
*
* @param orderType
*
* 五个fragment就因为orderType参数不同
*/
public void requestOrder(String orderType) {
params.put("act", getActionName());
params.put("st", orderType);
params.put("token", Constants.getInstance().token);
params.setSuccessMsgWhat(MY_ORDER_CODE);
HttpTools.request(params, context, handler);
}
@Override
public String getActionName() {
// TODO Auto-generated method stub
return URLUtils.MEMBER_ORDER;
}
@Override
public void handMsg(Message msg) {
// TODO Auto-generated method stub
switch (msg.what)
{
case MY_ORDER_CODE:
MyOrderParentModel model = JsonToModel.getJsonArrayToModelAndPage(
msg, MyOrderParentModel.class);
if(iView!=null&&model!=null){
iView.sendModel(model,params);
}
break;
default:
break;
}
}
}
以上提到的几个model和interface就不发出来了,根据自己的需要去写,这些没有任何曝光价值,以下是项目中运行的结果:
接下来,我们再从理论上分析模板方法模式,模板模式指的是定义一个操作中算法的轮廓和骨架,而将一些步骤延迟放到子类里,模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
BaseOrderFragment (Abstract Template)
1、是抽象类,其实就是一个抽象模板,定义了一个或多个抽象操作,以便让子类实现。这些抽象操作叫做基本操作,它们是一个顶级逻辑的组成步骤。说白了,就是一个父类让子类继承,把一些相似的操作和步骤放在父类,提高代码的重用性。
2、定义并实现了一个模板方法。这个模板方法一般是一个具体方法,它给出了一个顶级逻辑的骨架,而逻辑的组成步骤在相应的抽象操作中,推迟到子类实现。顶级逻辑也有可能调用一些具体方法。说白了,其实就是父类中抽象出一个方法,让每个子类都去重写这个方法,在上述代码中,子类重写的那部分,即requestParams()。
OrderListFragment、PendingPayFragment (Concrete Template)
实现父类所定义的一个或多个抽象方法,每个父类可以有多个子类与之对应,每个子类可以给出这些抽象方法不同的实现,从而使得顶级逻辑的实现各不相同,说白了,就是上述实现过程中,因为传的st各不相同,返回的json数据内容之间的不同,最终导致每个fragment页面的展示也不同。
UML如下:
总结:这次运用模板模式完全属于意外,在这之前,我对模板模式了解并不多,只是写完后,觉得和模板模式的思想挺符合的,昨晚把《大话设计模式》中的模板模式章节读了一遍,确实运用的就是模板模式。在这之前,一个好朋友问我,在实际开发中,运用到哪些设计模式,我和他说,代码能力有限,用到的很少,其实,我们并不需要刻意去把某个功能套上设计模式,强加的设计模式,并不会让代码写的优雅,反而觉得不伦不类,只有实际开发中运用到了,也确实需要这种模式,当你写完的时候,也许你也会会心一笑。