Fork me on GitHub

设计模式学习笔记——状态模式

前言

许久没有写博客了,最近有一点做咸鱼的倾向,Android方面也碰上瓶颈挺久了。主要是目前可能没有碰上特别有挑战的需求吧,以之前的知识储备就足够了。最近游戏进度倒是喜人,黑魂3快通关了,但也不能老做一条咸鱼,还是得进步啊~看书还是得做做笔记的,不然看了没多久可能就忘了,书携带也不带方便,所以还是做一下笔记发在自己的博客上比较稳妥。

本文以及后续所有的设模式博文均为《Android源码设计模式》一书的笔记。

状态模式介绍与定义

以下为书中原文:状态模式中的行为是由状态来决定的,不同的状态下有不同的行为。状态模式把对象的行为包装在不同的状态对象里,每一个状态对象都有一个共同的抽象状态基类。状态模式的意图是让一个对象在其内部状态改变的时候,其行为也随之改变。

  • 定义:当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。

介绍与定义都看了,该怎么理解呢?结合实际的例子来说,在用户登录与不登录时,一个按钮触发的点击事件是不一样的,这里登录与不登录是两种状态。那么不使用状态模式的实现是怎样的呢?

1
2
3
4
5
6
7
btn.setOnClickListener(v -> {
if(User.getInstance().isLogin) {
shareToQQ();
} else {
login();
}
});

这里一般来说会在一个用户对象里维护一个是否登录的标志位,这里就是通过这个标志位来判断用户是否登录。但是如果有非常多的状态需要判断,代码就会显得非常的杂乱,那么该通过怎样的方式才能避免使用大量的if-else(或者switch-case)呢?答案就是使用状态模式。

这里依旧使用登录来作为例子,应用场景:当用户登录时,点击分享按钮,会将内容分享到QQ,如果用户未登录,则会跳转至登录界面,让用户去登录。这里首先定义一个用户操作的接口,这个接口是所有状态下的状态类的基类:

1
2
3
public interface UserOperation {
void shareToQQ();
}

在定义了这个接口之后,需要两种状态下的实现类,首先编写登录状态下的实现类:

1
2
3
4
5
6
public class UserOperationLoginImpl implements UserOperation {
@Override
public void shareToQQ() {
System.out.println("已登录,直接分享到QQ");
}
}

这里代码实现比较简单,只是 Java 中的模拟实现,再编写未登录状态下的实现类:

1
2
3
4
5
6
7
8
9
10
11
public class UserOperationLogoutImpl implements UserOperation {
@Override
public void shareToQQ() {
System.out.println("未登录");
toLogin();
}

private void toLogin(){
System.out.println("去登录");
}
}

最后编写用户类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class User {
// 登录状态
public static final int STATE_LOGIN = 1;
// 未登录状态
public static final int STATE_LOGOUT = 2;

private int state = STATE_LOGOUT;
private UserOperation userOperation = new UserOperationLogoutImpl();

private User(){}

private static User user = new User();

public static User getInstance(){
return user;
}

public void setUserOperation(UserOperation userOperation){
this.userOperation = userOperation;
}

public void login(){
state = STATE_LOGIN;
setUserOperation(new UserOperationLoginImpl());
}

public void logout(){
state = STATE_LOGOUT;
setUserOperation(new UserOperationLogoutImpl());
}

public void shareToQQ(){
userOperation.shareToQQ();
}
}

这样就编写完毕了,再来编写一个测试用例:

1
2
3
4
5
6
7
8
9
public class Client {
public static void main(String... args){
User.getInstance().login();
User.getInstance().shareToQQ();
System.out.println("========================");
User.getInstance().logout();
User.getInstance().shareToQQ();
}
}

输出结果:
输出结果

这里从新手的角度来理解一下状态模式,状态模式其实就是使用多态来避免大量的使用 if-else 语句。在 User 类中持有一个用户操作接口 UserOperation 的引用,之后的用户调用都通过这个引用来去调用。而这个接口的实现类可以由用户设置,也可以通过调用登录或者登出自动去赋值的。这样就做到了在运行时根据状态改变自身的行为,而又避免了大量使用 if-else 语句。

在实际的情况下,一个事件的触发可能会由不止一种的标志决定,所以在实际的使用时可能还需要结合自身的情况去合理使用状态模式。