状态设计模式抽象一系列操作到一个特定的状态类中,这样避免使用if else或者switch case去区分不同状态逻辑所导致的代码逻辑臃肿,耦合性高.
例如目前有个电视遥控器,它有两种状态,一种开机状态,一种关机状态,它还具有一些操作,例如切换频道和调整音量大小,在开机状态下这下操作才能顺利执行,在关机状态下是不能执行的。于是代码如下:
public class TvController {
//开机状态
private final static int POWER_ON = 1;
//关机状态
private final static int POWER_OFF = 2;
private int mState = POWER_OFF;
public void powerOn(){
mState = POWER_ON;
if (mState == POWER_OFF){
System.out.println("开机啦");
}
}
public void powerOff(){
mState = POWER_OFF;
if (mState == POWER_ON){
System.out.println("关机啦");
}
}
public void nextChannel(){
if (mState == POWER_ON){
System.out.println("下一频道");
}else {
System.out.println("两个红灯提示没有开机");
}
}
public void prevChannel(){
if (mState == POWER_ON){
System.out.println("上一频道");
}else {
System.out.println("两个红灯提示没有开机");
}
}
public void turnUp(){
if (mState == POWER_ON){
System.out.println("调高音量");
}else{
System.out.println("两个红灯提示没有开机");
}
}
public void turnDown(){
if (mState == POWER_ON){
System.out.println("调低音量");
}else{
System.out.println("两个红灯提示没有开机");
}
}
}
可以看到在每次操作nextChannel,prevChannel,turnUp,turnDown时候都要进行if else的判断,如果我们再增加一种待机状态的化,还会增加else if语句,这样每次在增加操作的或者状态的时候都要去修改TvController这个类,这不符合开闭原则,而且容易写代码遗漏某些状态case. 状态模式就是用来解决这个问题的。
先看下状态模式的uml
通过抽象所有操作到IState接口中,实现类为不同状态,在不同状态下作出不同的实际操作,Context是一个外部调用用来设置状态,和调用不同操作的类,实际上它也可以实现IState接口。说了这么多,可能还比较抽象,下面看看优化过后的代码。
对电视遥控器的状态操作进行抽象
public interface TvState {
void nextChannel();
void prevChannel();
void turnUp();
void turnDown();
}
状态抽象类的实现类,在不同状态下实际的操作不一样
public class PowerOnState implements TvState {
@Override
public void nextChannel() {
System.out.println("下一频道");
}
@Override
public void prevChannel() {
System.out.println("上一频道");
}
@Override
public void turnUp() {
System.out.println("调高音量");
}
@Override
public void turnDown() {
System.out.println("调低音量");
}
}
public class PowerOffState implements TvState {
@Override
public void nextChannel() {
}
@Override
public void prevChannel() {
}
@Override
public void turnUp() {
}
@Override
public void turnDown() {
}
}
对电视遥控器的状态进行抽象(注意之前TvState抽象的是状态操作,这里抽象的是状态)
public interface PowerState {
void powerOn();
void powerOff();
}
最后的遥控器类
public class TvController implements PowerState,TvState {
private TvState tvState;
@Override
public void powerOn() {
tvState = new PowerOnState();
System.out.println("开机啦");
}
@Override
public void powerOff() {
tvState = new PowerOffState();
System.out.println("关机啦");
}
@Override
public void nextChannel() {
tvState.nextChannel();
}
@Override
public void prevChannel() {
tvState.prevChannel();
}
@Override
public void turnUp() {
tvState.turnUp();
}
@Override
public void turnDown() {
tvState.turnDown();
}
}
调用实现:
TvController tvController = new TvController();
tvController.powerOn();
tvController.nextChannel();
tvController.turnDown();
tvController.powerOff();
tvController.nextChannel();
最后输出:
01-12 22:16:07.135 19159-19159/com.example.huangli.statepattern I/System.out: 开机啦
01-12 22:16:07.135 19159-19159/com.example.huangli.statepattern I/System.out: 下一频道
01-12 22:16:07.135 19159-19159/com.example.huangli.statepattern I/System.out: 调低音量
01-12 22:16:07.135 19159-19159/com.example.huangli.statepattern I/System.out: 关机啦
上面讲述一个电视遥控器使用状态模式的一个简单栗子,它把在抽象电源打开状态和关闭状态不同的操作到不同的类中,从而规避了使用if else去判断状态。
什么时候可以使用到这种模式?
只要需要去判断不同状态下面作出不同的操作,并且这几种状态都抽象出一系列操作我们就可以使用这一种模式,举一个实际在安卓开发过程运用状态模式的栗子。
在浏览微博的时候,对感兴趣的微博可以转发,但是在登录和未登陆的情况下操作不一样,登录的情况下我可以直接转发微博,未登录的情况下需要跳转登录页面,评论功能同理。这里就符合使用状态模式的条件,在做出转发和评论的时候需要区分登录和未登录状态,并且登录和未登录状态都能抽象出一系列操作(转发和评论)。
先看下ui
当点击转发的时候如果在登录状态下就提示转发成功,未登录的状态就跳转登录画面.
具体代码实现:
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.forward_btn).setOnClickListener(this);
findViewById(R.id.login_btn).setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.forward_btn:
LoginContext.getLoginContext().forward(this);
break;
case R.id.login_btn:
LoginContext.getLoginContext().setState(new LogoutState());
break;
}
}
}
public class LoginActivity extends AppCompatActivity {
private EditText username;
private EditText password;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
username = findViewById(R.id.username_et);
password = findViewById(R.id.password_et);
findViewById(R.id.login_btn).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
login();
finish();
}
});
}
private void login(){
String userName = username.getText().toString().trim();
String passWord = password.getText().toString().trim();
//发送网络请求登陆
LoginContext.getLoginContext().setState(new LoginedState());
Toast.makeText(getApplicationContext(),"登录成功",Toast.LENGTH_LONG).show();
}
}
对状态操作进行抽象
public interface UserState {
/**
* 转发
*/
void forward(Context context);
/**
* 评论
*/
void comment(Context context);
}
public class LoginedState 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);
}
}
Context类供客户端调用
public class LoginContext implements UserState{
UserState mState = new LogoutState();
static LoginContext sLoginContext = new LoginContext();
private LoginContext() {
}
public static LoginContext getLoginContext(){
return sLoginContext;
}
public void setState(UserState userState){
mState = userState;
}
@Override
public void forward(Context context) {
mState.forward(context);
}
@Override
public void comment(Context context) {
mState.comment(context);
}
}
另外再举一个栗子,目前有款下载上传文件的app, 在3g,wifi,和无网络状态下它们下载和上传的操作也不一样,3g,wifi,和无网络状态下我们可以抽象出来上传和下载的操作,这里同样符合使用状态模式的要求。