状态机的实现场景在于不同的状态下对于同一行为有着不同的响应,即将if-else用多态来实现。通常情况下if-else火switch-case形式下根据不同的状态进行判断,如果是状态A就执行A响应,如果是状态B就执行B响应,但这种实现使得所有的逻辑耦合在一起,一旦状态较多并且状态之间有先后制约关系,就会很容易出错,通过状态机模式能够很好的避免这种问题。并且使代码更加清晰明了。
下面简单实现一个用户登录系统:
例如微博页面用户在未登录情况下点击转发按钮,此时会让用户先进行登录,然后执行转发动作,如果是已登录状态,用户可以直接进行转发操作。在登录和未登录的两种状态下对于转发这一操作有着不同的处理,当登录状态发生改变后转发的操作行为也发生了改变。
Demo中有MainActivity,LoginActivity,MainActivity有转发和注销登录功能,LoginAcitity为登录界面:
首先用户的默认状态为未登录状态,此时用户点击转发按钮会先跳转到登录界面执行登录,登录完成后回到MainActivity页面,此时用户点击转发可以直接进行转发。
MainActivity代码如下:
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//转发
findViewById(R.id.forward_btn).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
LoginContext.getLoginContext().forward(MainActivity.this);
}
});
//注销
findViewById(R.id.logout_btn).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
LoginContext.getLoginContext().setmState(new LogoutState());
}
});
}
}
LoginActivity是在用户输入用户名和密码后执行登录,成功之后将LoginContext的状态设置为登录状态,并且返回MainActivity页面,代码如下:
public class LoginActivity extends AppCompatActivity {
private EditText userNameEditText;
private EditText pswEditText;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
userNameEditText=(EditText)findViewById(R.id.usename);
pswEditText=(EditText)findViewById(R.id.psw);
//登陆按钮
findViewById(R.id.login).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
login();
finish();
}
});
}
private void login() {
String username = userNameEditText.getText().toString().trim();
String psw = pswEditText.getText().toString().trim();
//执行登陆网络请求
//登陆成功后将状态修改为登陆状态
LoginContext.getLoginContext().setmState(new LogoutState());
}
}
LoginContext是状态机的状态管理类,LoginContext将用户的相关操作委托给具体执行动作的状态类,当状态改变后(即LoginContext.getLoginContext().setmState(new LogoutState())),LoginContext的状态就
发生了改变,转发操作也发生了改变。代码如下:
public class LoginContext {
//用户状态 默认为未登录状态
UserState mState = new LogoutState();
//单例
static LoginContext sLoginContext = new LoginContext();
private LoginContext(){};
public static LoginContext getLoginContext(){
return sLoginContext;
}
public void setmState(UserState userState){
mState = userState;
}
//转发
public void forward(Context context){
mState.forward(context);
}
//评论
public void comment(Context context){
mState.comment(context);
}
}
LoginContext通过setState来对状态进行修改,并将操作委托给操作对象,不同的状态对象对于同一个操作进行了不同的处理,状态对象相关代码如下:
/**
* 用户状态
*/
public interface UserState {
/**
* 登陆
* @param context
*/
public void forward(Context context);
/**
* 分享
* @param context
*/
public void comment(Context context);
}
/**
* 登录状态
*/
public class LoginState implements UserState {
@Override
public void forward(Context context) {
Toast.makeText(context,"转发消息",Toast.LENGTH_LONG).show();
}
@Override
public void comment(Context context) {
Toast.makeText(context,"评论消息",Toast.LENGTH_LONG).show();
}
}
/**
* 注销状态
*/
public class LogoutState implements UserState {
@Override
public void forward(Context context) {
gotoLoginActivity(context);
}
@Override
public void comment(Context context) {
gotoLoginActivity(context);
}
private void gotoLoginActivity(Context context) {
Intent intent = new Intent(context,LoginActivity.class);
context.startActivity(intent);
}
}
用户状态有两种:转发和评论。已登录状态下用户调用这两个函数会执行真正的转发和评论动作,未登录状态下则会直接跳转到登录界面。用户登录成功后转发和评论的行为也发生了改变。
状态机模式的优点:代码逻辑清晰,耦合性低,便于扩展;
状态机缺点:代码的实现类和对象个数比较多。