闲来无事,继续对项目进行重构。这段时间在研究设计模式,苦于没有地方实战,不过现在机会来了。
这个项目是一个平台的商家版,所以会涉及到订单,其中有一个订单详情的页面。就是从网络拉取数据进行数据展示,还有做一些简单的接单之类的逻辑
但是如此简单的页面却写了将近700行代码。。。其中主要原因是因为订单的状态太多,这中间多了一系列的逻辑判断,每种订单都有不同的显示状态,都要进行处理
这让我一下就想到了状态模式,设计模式这东西,用语言很难表达清楚,直接上代码吧。
首先看看原来的代码,由于代码比较多,就抽取一部分
/**
* 刷新页面ui,不同状态显示不同的页面效果
*
* @param type 订单类型
*/
private void refreshUI(int type, int userCancelType) {
Logger.e(TAG, "type:" + type);
cb_user_assess.setChecked(payStatus==3);
cb_user_assess.setDateVisiable(payStatus==3?View.VISIBLE: View.GONE);
switch (type) {
case Constants.INDENT_BOOK:
ll_user_cancel.setVisibility(View.GONE);
cb_had_receive.setChecked(false);
cb_on_service.setChecked(false);
cb_complete_service.setChecked(false);
ll_refuse_receive.setVisibility(View.VISIBLE);
break;
case Constants.INDENT_RECEIVED:
ll_user_cancel.setVisibility(View.GONE);
tv_contact.setVisibility(View.VISIBLE);
tv_contact.setText("联系客户");
isCall = true;
cb_had_receive.setChecked(true);
cb_on_service.setChecked(false);
cb_complete_service.setChecked(false);
tv_contact.setVisibility(View.VISIBLE);
tv_contact.setText(UIUtils.getString(R.string.contact_user));
ll_refuse_receive.setVisibility(View.GONE);
btn_receive.setVisibility(View.GONE);
btn_refuse.setText("查看客户位置");
btn_refuse.setBackgroundColor(UIUtils.getColor(R.color.update_location));
btn_refuse.setTextColor(UIUtils.getColor(R.color.white));
break;
case Constants.INDENT_CANCEL:
ll_user_cancel.setVisibility(View.VISIBLE);
tv_not_need_service.setVisibility(View.VISIBLE);
tv_contact.setVisibility(View.GONE);
if (userCancelType == 1) {
cb_had_receive.setChecked(true);
} else if (userCancelType == 2) {
cb_had_receive.setChecked(false);
}
cb_on_service.setChecked(false);
cb_complete_service.setChecked(false);
tv_business_refuse.setText("客户已取消订单");
ll_refuse_receive.setVisibility(View.GONE);
break;
case Constants.INDENT_REFUSE:
ll_user_cancel.setVisibility(View.VISIBLE);
tv_not_need_service.setVisibility(View.GONE);
cb_had_receive.setChecked(false);
cb_on_service.setChecked(false);
cb_complete_service.setChecked(false);
tv_contact.setVisibility(View.GONE);
tv_business_refuse.setText("拒绝接单");
ll_refuse_receive.setVisibility(View.GONE);
break;
case Constants.INDENT_ONSERVICE:
ll_user_cancel.setVisibility(View.GONE);
cb_had_receive.setChecked(true);
cb_on_service.setChecked(true);
cb_complete_service.setChecked(false);
tv_contact.setVisibility(View.VISIBLE);
tv_contact.setText("完成服务");
isCall = false;
Logger.e(TAG, "服务中");
ll_refuse_receive.setVisibility(View.GONE);
break;
case Constants.INDENT_COMPLETE:
ll_user_cancel.setVisibility(View.GONE);
cb_had_receive.setChecked(true);
cb_on_service.setChecked(true);
cb_complete_service.setChecked(true);
tv_contact.setVisibility(View.VISIBLE);
tv_contact.setText("联系客户");
isCall = true;
if(payStatus==0){
ll_refuse_receive.setVisibility(View.VISIBLE);
} else {
ll_refuse_receive.setVisibility(View.GONE);
}
btn_receive.setVisibility(View.GONE);
btn_refuse.setText("更改支付金额");
btn_refuse.setBackgroundColor(UIUtils.getColor(R.color.price));
btn_refuse.setTextColor(UIUtils.getColor(R.color.white));
break;
case Constants.INDENT_ASSESS:
ll_user_cancel.setVisibility(View.GONE);
cb_had_receive.setChecked(true);
cb_on_service.setChecked(true);
cb_complete_service.setChecked(true);
tv_contact.setVisibility(View.VISIBLE);
tv_contact.setText("联系客户");
isCall = true;
ll_refuse_receive.setVisibility(View.GONE);
break;
}
}
7种订单状态,写了一长串的逻辑,这样的代码,光看着就好累,阅读性太差,下面不多说,开始重构
首先定义个订单状态的接口、里面包含四个抽象方法
public interface IIndentState {
/**
* 刷新订单状态的方法
*
* @param ll_cancel 取消订单的状态和原因
* @param isv_had_receive 已接单
* @param isv_on_service 服务中
* @param isv_complete 已完成
* @param isv_had_pay 已支付
* @param ll_bottom 底部的按钮
*/
void refreshUi(LinearLayout ll_cancel, IndentStateView isv_had_receive,
IndentStateView isv_on_service, IndentStateView isv_complete,
IndentStateView isv_had_pay, LinearLayout ll_bottom, TextView tv_title_right);
/**
* 底部左边按钮的点击逻辑
*/
void onBottomLeftClick(long id, IndentDetailActivity activity, double lat, double lng, String user_phone, String order_id);
/**
* 底部右边的按钮的点击逻辑
*/
void onBottomRightClick(IndentDetailContract.View view);
/**
* 标题右边的点击逻辑
*/
void onTitleRightClick(String user_phone, IndentDetailActivity activity);
}
然后实现各种状态,首先定义个基类,实现一个重复的逻辑
public class BaseState implements IIndentState {
private LinearLayout ll_cancel;
private IndentStateView isv_had_receive;
private IndentStateView isv_on_service;
private IndentStateView isv_complete;
private LinearLayout ll_bottom;
protected TextView tv_not_need_service;
protected TextView tv_business_refuse;
protected Button btn_receive;
protected Button btn_refuse;
@Override
public void refreshUi(LinearLayout ll_cancel, IndentStateView isv_had_receive,
IndentStateView isv_on_service, IndentStateView isv_complete,
IndentStateView isv_had_pay, LinearLayout ll_bottom,TextView tv_title_right) {
this.ll_cancel = ll_cancel;
this.isv_had_receive = isv_had_receive;
this.isv_on_service = isv_on_service;
this.isv_complete = isv_complete;
this.ll_bottom = ll_bottom;
tv_not_need_service = (TextView) ll_cancel.findViewById(R.id.tv_not_need_service);
tv_business_refuse = (TextView) ll_cancel.findViewById(R.id.tv_business_refuse);
btn_receive = (Button) ll_bottom.findViewById(R.id.btn_receive);
btn_refuse = (Button) ll_bottom.findViewById(R.id.btn_refuse);
}
@Override
public void onBottomLeftClick(long id, IndentDetailActivity activity, double lat, double lng, String user_phone, String order_id) {
}
@Override
public void onBottomRightClick(IndentDetailContract.View view) {
}
@Override
public void onTitleRightClick(String user_phone, IndentDetailActivity activity) {
}
/**
* 设置订单的状态
*
* @param cancel_visibility 取消订单
* @param had_receive 已接单
* @param on_service 服务中
* @param complete 服务完成
* @param refuse_visibility 拒接
*/
public void setStatus(int cancel_visibility, boolean had_receive,
boolean on_service, boolean complete, int refuse_visibility) {
ll_cancel.setVisibility(cancel_visibility);
isv_had_receive.setChecked(had_receive);
isv_on_service.setChecked(on_service);
isv_complete.setChecked(complete);
ll_bottom.setVisibility(refuse_visibility);
}
}
接下来的各种状态,只要去继承basestate,实现具体的逻辑就可以了,下面看预约的状态
public class BookState extends BaseState {
@Override
public void refreshUi(LinearLayout ll_cancel, IndentStateView isv_had_receive, IndentStateView isv_on_service, IndentStateView isv_complete, IndentStateView isv_had_pay, LinearLayout ll_bottom, TextView tv_title_right) {
super.refreshUi(ll_cancel, isv_had_receive, isv_on_service, isv_complete, isv_had_pay, ll_bottom, tv_title_right);
setStatus(View.GONE, false, false, false, View.VISIBLE);
}
@Override
public void onBottomLeftClick(long id, IndentDetailActivity activity, double lat, double lng, String user_phone, String order_id) {
super.onBottomLeftClick(id, activity, lat, lng, user_phone, order_id);
// 拒接单的逻辑
Intent intent = new Intent(activity, RefuseIndentActivity.class);
intent.putExtra(Constants.INDENT_ID, id);
activity.startActivityForResult(intent, 100);
}
@Override
public void onBottomRightClick(IndentDetailContract.View view) {
super.onBottomRightClick(view);
// 接单的逻辑
view.showReceiveDialog();
}
}
以上是预约状态的具体实现、剩下还有待服务,服务中,待支付等状态。。。由于代码类似就不贴出来了,
每种状态都分离出一个类来去实现,就可以把activity里面的逻辑拆分开来
接着定义个indentcontext类,主要作用是作为设置状态的一个中介
public class IndentContext {
private IIndentState mIndentState = new BookState();
private static IndentContext mIndentContext;
private IndentContext() {
}
/**
* 获取IndentContext对象的方法
*
* @return IndentContext 对象
*/
public static IndentContext getInstance() {
if (mIndentContext == null) {
synchronized (IndentContext.class) {
if (mIndentContext == null) {
return mIndentContext = new IndentContext();
}
}
}
return mIndentContext;
}
/**
* 设置状态
*
* @param state 订单的状态
*/
public void setState(BaseState state) {
mIndentState = state;
}
/**
* 刷新订单状态的方法
*
* @param ll_cancel 取消订单的状态和原因
* @param isv_had_receive 已接单
* @param isv_on_service 服务中
* @param isv_complete 已完成
* @param isv_had_pay 已支付
* @param ll_bottom 底部的按钮
*/
public void refreshUI(LinearLayout ll_cancel, IndentStateView isv_had_receive,
IndentStateView isv_on_service, IndentStateView isv_complete,
IndentStateView isv_had_pay, LinearLayout ll_bottom, TextView tv_title_right) {
mIndentState.refreshUi(ll_cancel, isv_had_receive, isv_on_service,
isv_complete, isv_had_pay, ll_bottom, tv_title_right);
}
/**
* 底部左边按钮的点击逻辑
*/
public void onBottomLeftClick(long id, IndentDetailActivity activity, double lat, double lng, String user_phone, String order_id) {
mIndentState.onBottomLeftClick(id, activity, lat, lng, user_phone, order_id);
}
/**
* 底部右边的按钮的点击逻辑
*/
public void onBottomRightClick(IndentDetailContract.View view) {
mIndentState.onBottomRightClick(view);
}
/**
* 标题右边的点击逻辑
*/
public void onTitleRightClick(String user_phone, IndentDetailActivity activity) {
mIndentState.onTitleRightClick(user_phone, activity);
}
}
主要是调用state里的所有方法,同时可以更改状态,默认是预约状态
接着在订单详情的activity里面使用就可以了,上代码
先创建对象
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_indent_detail);
indentContext = IndentContext.getInstance();
initView();
initEvent();
}
接着,refreshui里面的逻辑就被简化成这样了
/**
* 刷新页面ui,不同状态显示不同的页面效果
*
* @param type 订单类型
*/
private void refreshUI(int type, int userCancelType) {
Logger.e(TAG, "type:" + type);
BaseState state = null;
switch (type) {
case Constants.INDENT_BOOK:
state = new BookState();
break;
case Constants.INDENT_RECEIVED:
state = new ReceivedState();
break;
case Constants.INDENT_CANCEL:
if (userCancelType == 1) {
state = new UserCancelState();
} else if (userCancelType == 2) {
state = new CancelBeforeReceivedState();
}
break;
case Constants.INDENT_REFUSE:
state = new BusinessRefuseState();
break;
case Constants.INDENT_ONSERVICE:
state = new OnServiceState();
break;
case Constants.INDENT_COMPLETE:
case Constants.INDENT_ASSESS:
if (payStatus == 3) {
state = new HadPayState();
} else {
state = new CompleteState();
}
break;
}
indentContext.setState(state);
indentContext.refreshUI(ll_user_cancel, cb_had_receive, cb_on_service, cb_complete_service,
cb_user_assess, ll_refuse_receive, tv_contact);
}
然后是点击事件的逻辑,也不需要写那么多的if。。。else 了
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.tv_phone:
// 打电话
if (TextUtils.isEmpty(user_phone)) {
return;
}
toCallPhone(user_phone);
break;
case R.id.tv_contact:
indentContext.onTitleRightClick(user_phone, this);
break;
case R.id.btn_receive:
// 显示接单的对话框
indentContext.onBottomRightClick(this);
break;
case R.id.btn_refuse:
indentContext.onBottomLeftClick(id, this, lat, lng, user_phone, order_id);
break;
case R.id.iv_back:
finish();
break;
}
}
这样看起来就舒服了很多,而且维护起来也很简单,以后有什么状态需要修改的,直接去修改state类就行了,不需要动activity里面的代码。
当然设计模式少不了的就是弊端,这里很显然就是类的膨胀,一下子多了十几个类,不过为了让代码简洁,更容易维护,我觉得是值得的