【状态模式】
提到状态,首先应该想到的就是编译原理中的状态机,状态机的一个典型的特征就是处于不同的状态时遇到同义词时执行的操作不同。
由此,我们可以很容易的类比出:当一个对象的行为取决于它的状态,并且它在处于不同状态下相同的行为执行的操作不同的话,我们就可以使用状态模式,将一种对象的n种状态封装成n个状态对象,该对象持有一个状态的引用,当执行某种操作导致状态发生变化时,将对象中状态的引用指向另一种状态,这样,对象的任意操作就只要委托给不同的状态对象即可。
状态模式的简单实例如下所示:
// 状态类
class State {
public void doSth();
}
class StateA implements State {
public void doSth() { System.out.println("StateA do"); }
}
class StateB implements State {
public void doSth() { System.out.println("StateB do"); }
}
// 上下文对象
class Context {
private State state;
public void setState(State state) { this.state = state; }
public void doSth() { this.state.doSth(); }
}
// 测试类
class Test {
public static void main(String[] args) {
Context context = new Context();
context.setState(new StateA());
context.doSth(); // StateA do
context.setState(new StateB());
context.doSth(); // StateB do
}
}
从代码的示例上看,我们可以知道:状态模式的关键点在于不同的状态对于同一个行为的不同相应,如果我们不这么做,那么我们就需要在大量的地方写上复杂的if-else语句,导致了逻辑的耦合,这是一个使用多态解决耦合问题的典型方案,而如果在不同的状态下有不同的行为,那么最好不要使用状态模式。
在我们日常的使用中,登录状态的保持就是一个非常典型的状态模式,以微博为例:用户在登录和不登录状态下都可以看,但是如果要转发就只能登录才能转发,但是这并不代表转发的按键不能点了,而是跳转到登录界面。
// 状态类型
interface State {
public void forward();
}
class LoginState implements State {
public void forward() { Log.i("转发"); }
}
class LogoutState implements State {
public void forward() { goToLoginActivity(); }
}
class UserStateManager {
private UserStateManager usm;
private State userState = new LogoutState();
public UserStateManager getInstance() {
if (usm != null) { return this.usm; }
else { return new UserStateManager(); }
}
public void forward() { this.userState.forward(); }
public void setState(userState) { this.userState = userState; }
}
public class MainActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) {
findViewById(R.id.forward_btn).setOnClickListener(new OnClickListener() {
public void onClick(View v) {
UserStateManager.getInstance().forward();
}
})
}
}
public class LoginActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) {
findViewById(R.id.login_btn).setOnClickListener(new OnClickListener() {
public void onClick(View v) {
UserStateManager.getInstance().setState(new LoginState());
finish();
}
})
}
}
app维持一个用于控制用户状态的单例(例如LoginManager),整个实例持有状态的引用,该引用可以指向两个值(LoginState和LoginOutState),当做需要判断用户权限的操作的时候只要检查这个状态即可。
【策略模式】
策略模式和状态模式基本一致,连UML类图都一模一样,在代码的实现上,这两者的主要不同就是 缺少setState函数,对于状态模式而言,它的主要作用就是对于一个对象复杂的状态变化和每一种状态的不同的行为模式,我们可以使用setState对状态进行精细的控制,以清楚的控制一个对象对外的功能。
而策略模式不同,它一般用于同一种行为的不同策略,即最后的输出一般是同一种类型的,而这也是被叫做策略模式的原因。