English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Detailed Explanation of State Design Pattern in Android Programming

This article describes an example of the state pattern in Android programming design. Shared for everyone's reference, as follows:

Introduction

The behavior in the state pattern is determined by the state, and different behaviors are present in different states. The structure of the state pattern and the strategy pattern is almost completely the same, but their purposes and essences are completely different. The behaviors in the state pattern are parallel and non-replaceable, while the behaviors in the strategy pattern are independent of each other and can be interchanged. In a word, the state pattern wraps the behaviors of an object into different state objects, each of which has a common abstract state base class. The intention of the state pattern is to let the behavior of an object change along with its internal state change.

II. Definition

Allowing the behavior of an object to change when its intrinsic state changes, making the object appear to have changed its class.

III. Usage Scenarios

(1) The behavior of an object depends on its state, and it must change its behavior according to the state at runtime.
(2(The code contains a large number of conditional statements related to the object's state, such as, an operation contains a large number of multi-branch statements (if-else or switch-(case), and these branches depend on the state of the object.

The State pattern puts each conditional branch into an independent class, making it possible for you to treat the state of an object as an object based on the object's own situation, which can change independently without depending on other objects, thus eliminating excessive and repetitive if statements through polymorphism.-else branch statements.

IV. UML Class Diagram of the State Pattern

UML Class Diagram:

Role Introduction:

Context: Environment class, defining the interfaces of interest to the client, maintaining an instance of a State subclass, which defines the current state of the object.

State: Abstract state class or state interface, defining one or more interfaces, representing the behavior under the state.

ConcreteStateA, ConcreteStateB: Concrete state classes, each concrete state class implements the interface defined in the abstract State, achieving different behaviors under different states.

V. Simple Example

Below, we will demonstrate the implementation of the State pattern with the example of a TV remote control. We first simply divide the state of the TV into power on state and power off state. In the power on state, operations such as channel switching and volume adjustment can be performed through the remote control, but pressing the power on button repeatedly is ineffective; in the power off state, operations such as channel switching, volume adjustment, and power off are ineffective, and only pressing the power on button will take effect. That is to say, the internal state of the TV determines the behavior of the remote control.

Firstly, let's look at the common implementation methods:

public class TVController {
  //Power on state
  private final static int POWER_ON = 1;
  //Power off state
  private final static int POWER_OFF = 2;
  //Default state
  private int mState = POWER_OFF;
  public void powerOn(){
    if(mState == POWER_OFF){
      System.out.println("The TV has turned on");
    }
    mState = POWER_ON;
  }
  public void powerOff(){
    if(mState ==POWER_ON){
      System.out.println("The television has been turned off");
    }
    mState = POWER_OFF;
  }
  public void nextChannel(){
    if(mState ==POWER_ON){
      System.out.println("Next channel");
    }
      System.out.println("No power on");
    }
  }
  public void prevChannel(){
    if(mState ==POWER_ON){
      System.out.println("Previous channel");
    }
      System.out.println("No power on");
    }
  }
  public void turnUp(){
    if(mState ==POWER_ON){
      System.out.println("Increase volume");
    }
      System.out.println("No power on");
    }
  }
  public void turnDown(){
    if(mState ==POWER_ON){
      System.out.println("Lower volume");
    }
      System.out.println("No power on");
    }
  }
}

It can be seen that each operation is performed by judging the current state, and some code is repeated. If the state and functions increase, it will be more and more difficult to maintain. At this time, the state pattern can be used as follows:

Television state interface:

/**
 * Television state interface, defines the television's operation functions
 *
 **/
public interface TVState {
  public void nextChannel();
  public void prevChannel();
  public void turnUp();
  public void turnDown();
}

Power-off state:

/**
 *
 * Power-off state, operations have no result
 *
 * */
public class PowerOffState implements TVState{
  @Override
  public void nextChannel() {
  }
  @Override
  public void prevChannel() {
  }
  @Override
  public void turnUp() {
  }
  @Override
  public void turnDown() {
  }
}

Power-on state:

/**
 *
 * Power-on state, operations are valid
 *
 * */
public class PowerOnState implements TVState{
  @Override
  public void nextChannel() {
    System.out.println("Next channel");
  }
  @Override
  public void prevChannel() {
    System.out.println("Previous channel");
  }
  @Override
  public void turnUp() {
    System.out.println("Increase volume");
  }
  @Override
  public void turnDown() {
    System.out.println("Lower volume");
  }
}

Power operation interface:

/**
 * Power operation interface
 *
 * */
public interface PowerController {
  public void powerOn();
  public void powerOff();
}

Television remote control:

/**
 * Television remote control
 *
 * */
public class TVController implements PowerController{
  TVState mTVState;
  public void setTVState(TVState mTVState){
    this.mTVState = mTVState;
  }
  @Override
  public void powerOn() {
    setTVState(new PowerOnState());
    System.out.println("The TV has been turned on");
  }
  @Override
  public void powerOff() {
    setTVState(new PowerOffState());
    System.out.println("The TV has been turned off");
  }
  public void nextChannel(){
    mTVState.nextChannel();
  }
  public void prevChannel(){
    mTVState.prevChannel();
  }
  public void turnUp(){
    mTVState.turnUp();
  }
  public void turnDown(){
    mTVState.turnDown();
  }
}

Call:

public class Client {
  public static void main(String[] args) {
    TVController tvController = new TVController();
    //Set PowerOn state
    tvController.powerOn();
    //Next channel
    tvController.nextChannel();
    //Increase volume
    tvController.turnUp();
    //Turn off the TV
    tvController.powerOff();
    //Decrease volume, which will not take effect at this time
    tvController.turnDown();
  }
}

The output result is as follows:

The TV has been turned on
Next channel
Increase volume
The TV has been turned off

In the above implementation, we abstracted a TVState interface, which contains all the functions to operate the TV. This interface has two implementation classes, namely the PowerOnState and PowerOffState. In the PowerOn state, the power-on function is invalid, which means that when the TV is already turned on, pressing the power-on button will not produce any response; while in the PowerOff state, only the power-on function is available, and other functions will not take effect. The same operation, such as the turnUp function to increase the volume, is invalid in the PowerOff state, and it will increase the volume of the TV in the PowerOn state, which means that the internal state of the TV affects the behavior of the TV remote control. The state pattern encapsulates these behaviors into state classes, forwards these functions to the state object during operation, and different states have different implementations. This way, the repetition and disorder are eliminated in the form of polymorphism.-else statement, which is the essence of the state pattern.

Six, the use in Android practice

1The login system, the way to handle events is determined according to whether the user is logged in or not.
2The Wi-Fi management, the processing of WiFi scan requests is different in different states.

Below, we will take the login system as an example to explain the use of state pattern in practice:

In Android development, encountering a login interface is very common, and the application of state design pattern in the login interface is very extensive. The operations of the user are different in the logged in and not logged in states. For example, the most common case is when playing Sina Weibo, the user can only complete the operations of commenting and forwarding Weibo when logged in; when the user is not logged in and needs to perform the operations of forwarding and commenting on Weibo, they need to enter the login interface to log in first before they can perform the operations. Therefore, for these two different situations, it is best to use state design pattern to design this example.

1The base class of the state

As we mentioned earlier, the principle of state design pattern is actually polymorphism. Here, we use the UserState interface to represent this base class, including the forward operation and comment state, the code is as follows:

public interface UserState {
  /**
   * Forwarding operation
   * @param context
   */
  public void forword(Context context);
  /**
   * Comment operation
   * @param context
   */
  public void commit(Context context);
}

2The implementation classes LoginState and LogoutState for the two situations of logged in and not logged in users; the code is as follows:

In LoginState.java, the user can perform forward and comment operations.

public class LoginState implements UserState{
  @Override
  public void forword(Context context) {
    Toast.makeText(context, "Forward successful", Toast.LENGTH_SHORT).show();
  }
  @Override
  public void commit(Context context) {
    Toast.makeText(context, "Comment successful", Toast.LENGTH_SHORT).show();
  }
}

In LogoutState.java, the user is not allowed to perform operations when not logged in, but should jump to the login interface to log in first before they can perform operations.

public class LogoutState implements UserState{
  /**
   * Jump to the login interface to log in before you can forward
   */
  @Override
  public void forword(Context context) {
    gotoLohinActivity(context);
  }
  /**
   * Jump to the login interface to log in before you can comment
   */
  @Override
  public void commit(Context context) {
    gotoLohinActivity(context);
  }
  /**
   * interface jump operation
   * @param context
   */
  private void gotoLohinActivity(Context context){
    context.startActivity(new Intent(context, LoginActivity.class));
  }
}

3Operation role LoginContext

Here, the LoginContext acts as the Context role in the state pattern, being the user operation object and the management object. The LoginContext delegates the relevant operations to the state object, and when the state changes, the behavior of the LoginContext also changes. The code of the LoginContext is as follows*Below:

Tips:

Here we use the singleton to ensure that there is only one LoginContext globally to control the user status;

public class LoginContext {
  //The default user status is the unlogged status
  UserState state = new LogoutState();
  private LoginContext(){};//Private constructor to prevent external access via new to obtain the object
  //Singleton pattern
  public static LoginContext getInstance(){
   return SingletonHolder.instance;
  }
  /**
  *static code block
  */
  private static class SingletonHolder{
   private static final LoginContext instance = new LoginContext();
  }
  public void setState(UserState state){
    this.state = state;
  }
  //forward
  public void forward(Context context){
    state.forward(context);
  }
  //comment
  public void commit(Context context){
    state.commit(context);
  }
}

4interface display

LoginActivity.java, this interface performs login operations, after login, set LoginContext.getInstance().setState(new LoginState()); to login state, in MainActivity, the operations executed are the operations under the login state, that is, it can forward and comment;

public class LoginActivity extends Activity implements OnClickListener{
  private static final String LOGIN_URL = "http://10.10.200.193:8080/Day01/servlet/LoginServlet";
  private EditText et_username;
  private EditText et_password;
  private Button btn_login;
  private String username;
  private String password;
  private KJHttp http;
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_login);
    initView();
    initData();
  }
  private void initView() {
    et_username = (EditText) findViewById(R.id.et_username);
    et_password = (EditText) findViewById(R.id.et_password);
    btn_login = (Button) findViewById(R.id.btn_login);
    btn_login.setOnClickListener(LoginActivity.this);
  }
  private void initData() {
    http = new KJHttp();
  }
  /**
   * perform login operation
   *
   * @param username2
   * @param password2
   */
  protected void sendLogin(String username2, String password2) {
    HttpParams params = new HttpParams();
    params.put("username", "user1");
    params.put("password", "123456");
    http.post(LOGIN_URL, params, new HttpCallBack() {
      @Override
      public void onSuccess(String t) {
        if ("2"00".equals(t)) {
          //Set to logged-in state
          LoginContext.getInstance().setState(new LoginState());
          startActivity(new Intent(LoginActivity.this,MainActivity.class));
          finish();
          Toast.makeText(LoginActivity.this, "Login successful", Toast.LENGTH_SHORT).show();
        }
      }
    });
  }
  @Override
  public void onClick(View v) {
    switch (v.getId()) {
    case R.id.btn_login:
      username = et_username.getEditableText().toString().trim();
      password = et_password.getEditableText().toString().trim();
      if (TextUtils.isEmpty(username) || TextUtils.isEmpty(password)) {
        Toast.makeText(LoginActivity.this, "Username and password cannot be empty", Toast.LENGTH_SHORT).show();
        return;
      }
      sendLogin(username, password);
      break;
    }
  }
}

MainActivity.java, after the user logs in successfully, clicking on 'forward' and 'comment' performs operations under the logged-in state. When the user logs out, we set the state of LoginContext to unlogged state; LoginContext.getInstance().setState(new LogoutState()); At this point, when clicking on 'forward' and 'comment' operations, it will jump to the user login interface.

public class MainActivity extends Activity {
  private Button btn_forward;
  private Button btn_commit;
  private Button btn_logout;
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    initView();
    initListener();
  }
  private void initView() {
    btn_forward = (Button) findViewById(R.id.btn_forward);
    btn_commit = (Button) findViewById(R.id.btn_commit);
    btn_logout = (Button) findViewById(R.id.btn_logout);
  }
  private void initListener() {
    //Forwarding operation
    btn_forward.setOnClickListener(new OnClickListener() {
      @Override
      public void onClick(View v) {
        //Call the forwarding function in LoginContext
        LoginContext.getInstance().forward(MainActivity.this);
      }
    });
    //Comment operation
    btn_commit.setOnClickListener(new OnClickListener() {
      @Override
      public void onClick(View v) {
        //Call the forwarding function in LoginContext
        LoginContext.getInstance().commit(MainActivity.this);
      }
    });
    //Log out operation
    btn_logout.setOnClickListener(new OnClickListener() {
      @Override
      public void onClick(View v) {
        //Set to logout state
        LoginContext.getInstance().setState(new LogoutState());
      }
    });
  }
}

Chapter 7: Summary

The key point of the state pattern is that different behaviors have different responses under different states, which is actually a 'if' statement that is transformed into a 'switch' statement.-A specific example of implementing 'else' with polymorphism in the 'if' statement.-or 'else' that appears.-Under the 'case' form, judgment is made based on different states. If it is state A, execute method A; if it is state B, execute method B. However, this implementation makes the logic coupled together, easy to make mistakes, and the state pattern can effectively eliminate this kind of 'ugly' logic processing. Of course, it is not any 'if' or 'else' or 'switch'.-All places with 'else' should be refactored through the state pattern. The application of the pattern must consider the context in which you are and the problem you are solving. Only if it fits a specific scenario is it recommended to use the corresponding pattern.

Advantages:

Put all behaviors related to a specific state into a state object, which provides a better way to organize code related to a specific state, transforming the cumbersome state judgment into a clear state class family, avoiding code bloat while ensuring scalability and maintainability.

Disadvantages:

The use of the state pattern will inevitably increase the number of system classes and objects.

Readers who are interested in more about Android-related content can check the special topics on this site: 'Android Development入门与进阶教程', 'Android Debugging Skills and Common Problem Solving Methods Summary', 'Summary of Android Basic Component Usage', 'Summary of Android View View Skills', 'Summary of Android Layout Layout Skills', and 'Summary of Android Widget Usage'.

I hope the description in this article will be helpful to everyone's Android program design.

Declaration: The content of this article is from the Internet, and the copyright belongs to the original author. The content is contributed and uploaded by Internet users spontaneously. This website does not own the copyright, has not been manually edited, and does not assume any relevant legal responsibility. If you find any content suspected of copyright infringement, please send an email to: notice#w.3If you find any copyright-infringing content, please report by email to: notice#w. Replace # with @ when sending emails, and provide relevant evidence. Once verified, this site will immediately delete the suspected infringing content.

You May Also Like