设计模式

设计模式-状态模式

2020-10-15  本文已影响0人  h2coder

什么是状态模式?什么时候用?

状态模式一般用于行为是状态决定的场景,不同状态下的行为也不同。适合状态比较多(3,4个以上),方法调用时需要比较多的if-else或switch-case的情况。

怎么实现状态模式?

状态模式一般会有以下对象:

状态模式重构聊天室,限制状态切换

在项目中,实现了聊天室的基础功能,但是突然聊天室需要实现限制性功能,怎样为限制呢?
看以下分析:

  1. 正常状态。(可以正常聊天)
正常状态.png
  1. 关闭状态。(正常不会被关闭,除非被老师关闭,24小时候自动重开,或者老师手动选择恢复,这时转换到正常状态)。
关闭状态.png
  1. 用户发送5条消息,老师都未回复,被限制发送状态(只有老师回复才会解除,回复一次即可,以后永远不会触发该类型限制,除非老师再次关闭了聊天室,则会重新计算次数)。
限制发送状态.png

可以看到,上面有2种状态,分别是正常状态、关闭状态、限制发送状态。

而这3种状态,UI上也是不一样的,UI上的点击行为也是不一样的。

  1. 正常状态,没有限制,没有UI阻挡输入框。
  2. 关闭状态,有UI阻挡输入框,展示一行文字,并有下单付费咨询室按钮,点击跳转到老师个人中心。并且有倒计时进行24小时的倒计时。
  3. 限制发送状态,有UI阻挡输入框,展示一行文字,并且有下单付费咨询室的按钮,点击跳转个人中心。

而关闭状态和限制发送状态,是由后端触发,并发送WebSocket消息,客户端接收到后,进行Json解析,再处理UI。所以他们是异步的,这里我们使用RecyelrView进行实现。

为什么不用Fragment?因为在进入聊天室时,就要马上拉取状态决定是否显示某种UI,如果是接口拉取成功设置Fragment,而下一秒就需要拉取另外一个接口进行设置数据到Fragment(设置倒计时)中时就会出现异步问题,Fragment还未add到Activity,就进行Find操作,是找不到的,而且也是基于我们的状态变化,关闭状态可以转换回正常状态,限制发送状态在老师回复一句话后即可解除,这里如果是Fragment的话,切换也会存在问题(一开始设置为默认状态,拉取接口状态后马上切换为限制状态时,就会出现异步问题)。

而RecyclerView的ViewType和根据Model切换条目,刚好适合我们的需求,第一RecyclerView是直接布局上add,是同步的,不存在异步问题。拉取接口后,我们设置Model到Adapter的数据集后Notify即可,后续切换其他状态,直接清空数据集再add对应状态的Model再刷新即可~

最初的实现

其实一开始的实现并不好,当时只有一种限制发送和正常状态,没有想太多,只是将代码抽取到另外一个Agent类进行操作。到了关闭状态出现时,问题就出现了。每加一个状态,提供的方法就要成倍增加,慢慢Agent类就开始膨胀,慢慢出现了“烂代码”的坏味道。

public class ConsultRoomBanTipAgent {
    private RecyclerView vBanTipList;

    private Context mContext;
    private LifecycleOwner mLifecycleOwner;
    private Items mItems;
    private RAdapter mAdapter;
    private RoomCloseBanTipViewBinder.OnCloseReOpenCallback mOnCloseReOpenCallback;

    public Context getContext() {
        return mContext;
    }

    public void attachUI(Context context, LifecycleOwner lifecycleOwner, View layout) {
        mContext = context;
        mLifecycleOwner = lifecycleOwner;
        findView(layout);
        bindView();
    }

    private void findView(View view) {
        //限制占位布局
        vBanTipList = view.findViewById(R.id.ban_tip_list);
    }

    private void bindView() {
        //...省略RecyclerView的设置操作
        vBanTipList.setAdapter(mAdapter);
    }

     //设置倒计时结束时的回调
    public void setOnCloseReOpenCallback(RoomCloseBanTipViewBinder.OnCloseReOpenCallback onCloseReOpenCallback) {
        mOnCloseReOpenCallback = onCloseReOpenCallback;
    }

    /**
     * 显示限制提示
     */
    public void changeToLimitSendMsgTip(SendMsgLimitModel model) {
        mItems.clear();
        mItems.add(model);
        mAdapter.notifyDataSetChanged();
    }

    /**
     * 显示老师关闭聊天室提示
     */
    public void changeToTeacherCloseBanTip(RoomCloseModel model) {
        mItems.clear();
        mItems.add(model);
        mAdapter.notifyDataSetChanged();
    }

    /**
     * 隐藏限制布局
     */
    public void removeAllBanTip() {
        mItems.clear();
        mAdapter.notifyDataSetChanged();
    }
    
    //...以后再更多多张状态时,这里方法会成倍增加。
}
msgParser.registerOnReceiveMsgCallback(new SimpleOnReceiveMsgCallback() {
            @Override
            public void onReceiveRoomInfo(WssResponseModel<RoomInfoModel> response) {
                super.onReceiveRoomInfo(response);
                handleRoomInfo(response);
            }

            @Override
            public void onReceiveSendMsgLimitMsg(WssResponseModel<SendMsgLimitModel> response) {
                super.onReceiveSendMsgLimitMsg(response);
                //限制发送消息,切换到限制发送状态
                SendMsgLimitModel data = response.getData();
                setTeacherId(data);
                changeToLimitSendMsgBanTip(data);
            }

            @Override
            public void onReceiveSendMsgRemoveLimitMsg(WssResponseModel<SendMsgLimitModel> response) {
                super.onReceiveSendMsgRemoveLimitMsg(response);
                //解除限制发送消息,切换到普通状态
                removeAllBanTip();
            }

            @Override
            public void onReceiveCloseRoomMsg(WssResponseModel<RoomCloseModel> response) {
                super.onReceiveCloseRoomMsg(response);
                //老师关闭了咨询室
                if (getConsultTeacherInfo() == null) {
                    return;
                }
                    //切换到关闭状态
changeToTeacherCloseBanTip(ConsultRoomOpenStatus.CLOSE,
                        response.getData().getRoomCloseReOpenSeconds(),
                        getConsultTeacherInfo().getId());
            }

            @Override
            public void onReceiveReOpenRoomMsg(WssResponseModel<String> response) {
                super.onReceiveReOpenRoomMsg(response);
                //老师重新开启了咨询室,恢复普通状态
                removeAllBanTip();
            }
        });

实现步骤

由于状态添加导致Agent类膨胀,那么使用状态模式怎样实现呢?按照上面涉及到的对象,我们开始实现:

public interface BanTipState {
    /**
     * 开始依附
     *
     * @param lifecycleOwner 生命周期
     * @param items          列表数据集
     * @param adapter        列表适配器
     */
    void onAttachState(LifecycleOwner lifecycleOwner, Items items, RAdapter adapter);

    /**
     * 解除依附
     */
    void onDetachState();
}

正常状态,没有UI阻挡,可以自由聊天。

public class NormalBanTipState implements BanTipState {

    @Override
    public void onAttachState(LifecycleOwner lifecycleOwner, Items items, RAdapter adapter) {
        //没有UI阻挡,清空列表的条目即可
        items.clear();
        adapter.notifyDataSetChanged();
    }

    @Override
    public void onDetachState() {
    }
}

限制发送状态。老师回复一句话有,恢复到正常状态。

public class LimitSendMsgBanTipState implements BanTipState {
    private SendMsgLimitModel mModel;
    private SendMsgLimitBanTipViewBinder mViewBinder;

    public LimitSendMsgBanTipState(SendMsgLimitModel model) {
        mModel = model;
    }

    @Override
    public void onAttachState(LifecycleOwner lifecycleOwner, Items items, RAdapter adapter) {
        if (mViewBinder == null) {
            mViewBinder = new SendMsgLimitBanTipViewBinder(lifecycleOwner);
            adapter.register(SendMsgLimitModel.class, mViewBinder);
        }
        items.clear();
        items.add(mModel);
        adapter.notifyDataSetChanged();
    }

    @Override
    public void onDetachState() {
    }
}

关闭状态,具有倒计时,倒计时结束后恢复到正常状态

public class TeacherCloseRoomBanTipState implements BanTipState {
    private RoomCloseModel mModel;
    private RoomCloseBanTipViewBinder.OnCloseReOpenCallback mOnCloseReOpenCallback;
    private RoomCloseBanTipViewBinder mViewBinder;

    public TeacherCloseRoomBanTipState(RoomCloseModel model, RoomCloseBanTipViewBinder.OnCloseReOpenCallback onCloseReOpenCallback) {
        mModel = model;
        mOnCloseReOpenCallback = onCloseReOpenCallback;
    }

    @Override
    public void onAttachState(LifecycleOwner lifecycleOwner, Items items, RAdapter adapter) {
        if (mViewBinder == null) {
            mViewBinder = new RoomCloseBanTipViewBinder(lifecycleOwner,
                    new RoomCloseBanTipViewBinder.OnCloseReOpenCallback() {
                        @Override
                        public void onArriveReOpen() {
                            if (mOnCloseReOpenCallback != null) {
                                mOnCloseReOpenCallback.onArriveReOpen();
                            }
                        }

                        @Override
                        public void onCountDownError(Throwable error) {
                            if (mOnCloseReOpenCallback != null) {
                                mOnCloseReOpenCallback.onCountDownError(error);
                            }
                        }
                    });
            adapter.register(RoomCloseModel.class, mViewBinder);
        }
        items.clear();
        items.add(mModel);
        adapter.notifyDataSetChanged();
    }

    @Override
    public void onDetachState() {
        if (mViewBinder != null) {
            mViewBinder.removeCallback();
        }
        mOnCloseReOpenCallback = null;
    }
}

Context,环境类,管理所有状态

public class BanTipContext implements LifecycleObserver {
    private RecyclerView vBanTipList;
    private Context mContext;
    private LifecycleOwner mLifecycleOwner;
    private Items mItems;
    private RAdapter mAdapter;

    private BanTipState mCurrentState;

    /**
     * @param context        上下文
     * @param lifecycleOwner 生命周期
     * @param rootLayout     根布局
     */
    public BanTipContext(Context context, LifecycleOwner lifecycleOwner, View rootLayout) {
        mContext = context;
        mLifecycleOwner = lifecycleOwner;
        mCurrentState = new NormalBanTipState();
        findView(rootLayout);
        bindView();
        mLifecycleOwner.getLifecycle().addObserver(this);
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    protected void onLifecycleDestroy() {
        if (mCurrentState != null) {
            mCurrentState.onDetachState();
        }
    }

    private void findView(View rootLayout) {
        vBanTipList = rootLayout.findViewById(R.id.ban_tip_list);
    }

    private void bindView() {
        vBanTipList.setLayoutManager(new LinearLayoutManager(mContext));
        mItems = new Items();
        mAdapter = new RAdapter(mItems);
        vBanTipList.setAdapter(mAdapter);
    }

    public void setState(BanTipState newState) {
        mCurrentState.onDetachState();
        mCurrentState = newState;
        mCurrentState.onAttachState(mLifecycleOwner, mItems, mAdapter);
    }
}

消息监听,对应回调切换到对应的状态

//环境类对象
mBanTipContext = new BanTipContext(getContext(), mLifecycleOwner, vRootView);

msgParser.registerOnReceiveMsgCallback(new SimpleOnReceiveMsgCallback() {
            @Override
            public void onReceiveRoomInfo(WssResponseModel<RoomInfoModel> response) {
                super.onReceiveRoomInfo(response);
                handleRoomInfo(response);
            }

            @Override
            public void onReceiveSendMsgLimitMsg(WssResponseModel<SendMsgLimitModel> response) {
                super.onReceiveSendMsgLimitMsg(response);
                //限制发送消息
                if (getConsultTeacherInfo() == null) {
                    return;
                }
                SendMsgLimitModel model = response.getData();
                setTeacherId(model);
                //设置为限制发送消息状态
                mBanTipContext.setState(new LimitSendMsgBanTipState(model));
            }

            @Override
            public void onReceiveSendMsgRemoveLimitMsg(WssResponseModel<SendMsgLimitModel> response) {
                super.onReceiveSendMsgRemoveLimitMsg(response);
                //解除限制发送消息,恢复到普通状态
                mBanTipContext.setState(new NormalBanTipState());
            }

            @Override
            public void onReceiveCloseRoomMsg(WssResponseModel<RoomCloseModel> response) {
                super.onReceiveCloseRoomMsg(response);
                //老师关闭了咨询室,设置为关闭状态
                    mBanTipContext.setState(new TeacherCloseRoomBanTipState(
                new RoomCloseModel(teacherUid, roomReOpenSeconds),
                new RoomCloseBanTipViewBinder.OnCloseReOpenCallback() {
                    @Override
                    public void onArriveReOpen() {
                        removeAllBanTip();
                    }

                    @Override
                    public void onCountDownError(Throwable error) {
                        error.printStackTrace();
                        L.d("咨询室关闭后,重新开启倒计时出现异常: " + error.getMessage());
                    }
                }));
            }

            @Override
            public void onReceiveReOpenRoomMsg(WssResponseModel<String> response) {
                super.onReceiveReOpenRoomMsg(response);
                //老师重新开启了咨询室,恢复到正常状态
                mBanTipContext.setState(new NormalBanTipState());
            }

            private void setTeacherId(SendMsgLimitModel model) {
                model.setTeacherUid(getConsultTeacherInfo().getId());
            }
        });

总结

状态模式的其他场景。

例如订单模块,未支付、已支付、待评价、已评价等状态,同样他们的行为会不一致:

再或者审核状态,待审核,审核成功、审核失败等。

上一篇 下一篇

猜你喜欢

热点阅读