Location>code7788 >text

Design patterns of the state pattern (three minutes to learn a design pattern)

Popularity:951 ℃/2024-09-12 10:25:51

The State Pattern is defined like this:The behavior of a class is changed based on its state.
Note that state here does not narrowly mean that the object maintains a "state" field, but that the overall behavior of the object (external methods) changes when we pass in different enumeration values.
Rather, it means that if an internal (arbitrary) field changes, then its state changes, and then its external representation changes.
It is one of the 23 object-oriented design patterns and belongs to the scope of behavioral patterns.
Usually when we address the different behaviors of external methods in different states, we can define a number of enumerations, and then write a bunch of if, elseif, switch, and other selection commands to differentiate between the different states, and then go through different business branches.
The state pattern, on the other hand, supports abstracting these branching businesses into a separate class (state class), and we can dynamically execute different business methods by passing in different state classes.
The overall structure is roughly like this:

The business class maintains an internal state object that supports switching to a different state object by external passes.
And these state objects are unified implementation of specific methods, the business class in the implementation of business methods, will call these state objects in the implementation of the method. (Anti-theft connection: this article was first published from /jilodream/ ) so that when switching states, the business methods will call different state object methods. From an object-oriented point of view, the synchronization of state change and class behavior is achieved.
Take a look at a specific code example:

enumerated class (math.)

1 package ;
2 
3 public enum TextStatusEnum {
4     ONLY_READ,
5     READ_WRITE,
6     UNAVAILABLE;
7 
8 }

State Definition Interface

 1 package ;
 2 
 3 /**
 4  * @discription
 5  */
 6 public interface TextState {
 7      TextStatusEnum getStatus();
 8 
 9      void write(String content);
10 
11      void clear();
12 
13      String read();
14 
15      void setContent(StringBuilder sb);
16 }

read-only state (computing)

 1 package ;
 2 
 3 import ;
 4 import .slf4j.Slf4j;
 5 
 6 /**
 7  * @discription
 8  */
 9 @Slf4j
10 @Data
11 public class OnlyReadState implements TextState {
12     private static final TextStatusEnum textStatus = TextStatusEnum.ONLY_READ;
13 
14     private StringBuilder sb;
15 
16     @Override
17     public TextStatusEnum getStatus() {
18         return textStatus;
19     }
20 
21     public void write(String content) {
22         ("sorry, you can not write");
23     }
24 
25     public void clear() {
26         ("sorry, you can not clear");
27     }
28 
29     public String read() {
30         return ();
31     }
32 
33     @Override
34     public void setContent(StringBuilder sb) {
35         this.sb = sb;
36     }
37 }

read-write state

 1 package ;
 2 
 3 import ;
 4 import .slf4j.Slf4j;
 5 
 6 /**
 7  * @discription
 8  */
 9 @Data
10 @Slf4j
11 public class ReadWriteState implements TextState {
12     private static final TextStatusEnum textStatus = TextStatusEnum.ONLY_READ;
13 
14     private StringBuilder sb = new StringBuilder();
15 
16     @Override
17     public TextStatusEnum getStatus() {
18         return textStatus;
19     }
20 
21     public void write(String content) {
22         (content);
23     }
24 
25     public void clear() {
26         (0);
27     }
28 
29     public String read() {
30         return ();
31     }
32 
33     @Override
34     public void setContent(StringBuilder sb) {
35         this.sb = sb;
36     }
37 }

Editor of this article (business class/context)

 1 package ;
 2 
 3 import ;
 4 import .slf4j.Slf4j;
 5 
 6 /**
 7  * @discription
 8  */
 9 @Slf4j
10 public class TextEditor {
11 
12     private StringBuilder sb = new StringBuilder();
13 
14     private TextState textState;
15 
16     public void setState(TextState textState) {
17         (sb);
18         this.textState = textState;
19     }
20 
21     public void write(String content) {
22         if (textState == null) {
23             ("no state exist");
24             return;
25         }
26         (content);
27     }
28 
29     public void clear() {
30         if (textState == null) {
31             ("no state exist");
32             return;
33         }
34         ();
35     }
36 
37     public String read() {
38         if (textState == null) {
39             ("no state exist");
40             return "no state";
41         }
42         return ();
43     }
44 
45 }

main category

 1 package ;
 2 
 3 import .slf4j.Slf4j;
 4 
 5 /**
 6  * @discription
 7  */
 8 @Slf4j
 9 public class PatternMain {
10     public static void main(String[] args) {
11         TextEditor editor = new TextEditor();
12         String text;
13 
14         //read-write state
15         TextState rw = new ReadWriteState();
16         (rw);
17         for (int i = 0; i < 3; i++) {
18             ("write" + i);
19             text = ();
20             ("read :" + text);
21         }
22         ();
23         text = ();
24         ("after clear, we read :" + text);
25         ("last write");
26 
27         ("-----------------------now, we exchange state to only read-----------------------" );
28         //read-only state (computing)
29         TextState or = new OnlyReadState();
30         (or);
31         for (int i = 0; i < 3; i++) {
32             ("write" + i);
33             text = ();
34             ("read :" + text);
35         }
36         ();
37         text = ();
38         ("after clear, we read :" + text);
39     }
40 }

The output effect is as follows:

10:02:52.356 [main] WARN  - read :write0
10:02:52.368 [main] WARN  - read :write0write1
10:02:52.369 [main] WARN  - read :write0write1write2
10:02:52.371 [main] WARN  - after clear, we read :(anti-theft link: this article was first published in /jilodream/ )
10:02:52.372 [main] WARN  - -----------------------now, we exchange state to only read-----------------------
10:02:52.376 [main] ERROR  - sorry, you can not write
10:02:52.378 [main] WARN  - read :last write
10:02:52.378 [main] ERROR  - sorry, you can not write
10:02:52.378 [main] WARN  - read :last write
10:02:52.379 [main] ERROR  - sorry, you can not write
10:02:52.379 [main] WARN  - read :last write
10:02:52.379 [main] ERROR  - sorry, you can not clear
10:02:52.380 [main] WARN  - after clear, we read :last write

Process finished with exit code 0

We can see that after initially setting the read/write state, we can do read, write, and clear operations

After setting the read status then only read is possible.

In retrospect, we are actually encapsulating the selection branches of different if Switches, along with the selected state, into different state classes, so that if we need to add a new kind of branching logic, we no longer need to modify the selection branch, but only need to add a new state class.
That is whether the state pattern can replace the traditional if selection branch, the answer is no, essentially or a degree of reason, the face of the object if over-designed, will lead to an infinite expansion of the number of classes, it is difficult to maintain, imagine if there is a number of state fields (status, type, etc.), then the state of the entity object is a combination of a number of state fields, each new state field will be added to the cause the number of statuses to increase rapidly, which is obviously not what we want to see.