1, APP activity lottery question
Please write a program to complete the APP lottery. The specific requirements are as follows:
- If you want to deduct 50 points from users every time you participate in this activity, the winning probability is 10%;
- The number of prizes is fixed, and you can't draw after drawing;
- The activity has four states: lottery, no lottery, prize distribution and prize collection;
- The four state transition diagrams of the activity are as follows:
2, State mode
2.1 Preface
In the process of software development, some objects in the application may make different behaviors according to different situations. We call this kind of object stateful object, and one or more dynamically changing attributes that affect the behavior of the object state. When stateful objects interact with external events, their internal state will change, so that their behavior will also change. If people are happy and sad, different emotions have different behaviors. Of course, the outside world will also affect their emotional changes.
For this stateful object programming, the traditional solution is to consider all these possible situations, and then use if else or switch case statements to judge the state, and then deal with different situations. However, it is obvious that this method has natural disadvantages for complex state judgment. The conditional judgment statements will be too bloated, poor readability, non extensible and difficult to maintain. When adding a new state, a new if else statement should be added, which violates the "opening and closing principle" and is not conducive to the expansion of the program.
The above problems can be well solved if the "state mode" is adopted. The solution idea of state mode is: when the conditional expression controlling the state transition of an object is too complex, extract the relevant "judgment logic" and represent it with different classes. In which case the system is in, directly use the corresponding state class objects for processing, so as to simplify the original complex logic judgment and eliminate if else Redundant statements such as switch case make the code more hierarchical and have good scalability.
2.2 introduction to status mode
Definition of State mode: for stateful objects, complex "judgment logic" is extracted into different State objects, allowing State objects to change their behavior when their internal State changes;
State mode is an object behavior mode, and its main advantages are as follows.
- The structure is clear, and the state mode localizes the behaviors related to a specific state into one state, and separates the behaviors in different states to meet the "single responsibility principle".
- Display the state transition to reduce the interdependence between objects. Introducing different states into independent objects will make the state transition more explicit and reduce the interdependence between objects.
- The responsibilities of the status class are clear, which is conducive to the expansion of the program. It is easy to add new states and transitions by defining new subclasses.
The main disadvantages of state mode are as follows.
- The use of state mode will inevitably increase the number of classes and objects in the system.
- The structure and implementation of state mode are complex. Improper use will lead to confusion of program structure and code.
- The state mode does not support the opening and closing principle very well. For the state mode that can switch states, adding a new state class requires modifying the source code responsible for state transformation, otherwise it cannot switch to the new state, and modifying the behavior of a state class also requires modifying the source code of the corresponding class.
2.3 implementation class diagram of structure pattern
State mode packages the behavior of objects changed by the environment in different state objects. Its intention is to make an object change its behavior when its internal state changes. Now let's analyze its basic structure and implementation method.
The status mode contains the following main roles.
- Context role: also known as context, it defines the interface required by the client, internally maintains a current state, and is responsible for switching specific states.
- Abstract State role: define an interface to encapsulate the behavior corresponding to a specific State in an environment object. There can be one or more behaviors.
- Concrete State role: implement the behavior corresponding to the abstract state, and switch the state if necessary.
2.4 use status mode to solve the lottery problem of APP activities
The specific code implementation is as follows: abstract a state class, which contains methods for deducting points, winning or not, and distributing prizes. Each state has different implementations for these operations;
/** * State abstract class * * @author Administrator */ public abstract class State { // Deduct points - 50 public abstract void deductMoney(); // Do you win the prize public abstract boolean raffle(); // Distribute prizes public abstract void dispensePrize(); }
Then the corresponding state class implementation;
/** * Cannot draw status * * @author Administrator */ public class NoRaffleState extends State { // The activity reference is passed in during initialization, and its state is changed after deducting the points RaffleActivity activity; public NoRaffleState(RaffleActivity activity) { this.activity = activity; } // Points can be deducted in the current status. After deduction, the status can be set to lottery status @Override public void deductMoney() { System.out.println("After deducting 50 points successfully, you can draw a lottery"); activity.setState(activity.getCanRaffleState()); } // Current status cannot draw @Override public boolean raffle() { System.out.println("Deduct points to draw!"); return false; } // Cannot send prizes in current status @Override public void dispensePrize() { System.out.println("No prizes can be awarded"); } }
/** * Status of awards issued * * @author Administrator */ public class DispenseState extends State { // The activity reference is passed in during initialization, and its status is changed after the prize is distributed RaffleActivity activity; public DispenseState(RaffleActivity activity) { this.activity = activity; } // @Override public void deductMoney() { System.out.println("Points cannot be deducted"); } @Override public boolean raffle() { System.out.println("No lucky draw"); return false; } //Distribute prizes @Override public void dispensePrize() { if (activity.getCount() > 0) { System.out.println("Congratulations on winning the prize"); // Change the status to no lucky draw activity.setState(activity.getNoRafflleState()); } else { System.out.println("Unfortunately, the prizes have been sent out"); // Change the status to send the prize, and then we can't draw the prize activity.setState(activity.getDispensOutState()); //System.out.println("end of lucky draw"); //System.exit(0); } } }
/** * Status of prize distribution completed * Note: when our activity is changed to spenseoutstate, the lucky draw ends * * @author Administrator */ public class DispenseOutState extends State { // Activity reference passed in during initialization RaffleActivity activity; public DispenseOutState(RaffleActivity activity) { this.activity = activity; } @Override public void deductMoney() { System.out.println("The prizes have been sent. Please join us next time"); } @Override public boolean raffle() { System.out.println("The prizes have been sent. Please join us next time"); return false; } @Override public void dispensePrize() { System.out.println("The prizes have been sent. Please join us next time"); } }
/** * The state of being able to draw * * @author Administrator */ public class CanRaffleState extends State { RaffleActivity activity; public CanRaffleState(RaffleActivity activity) { this.activity = activity; } //Points have been deducted and can't be deducted any more @Override public void deductMoney() { System.out.println("Points have been deducted"); } //You can draw the prize. After drawing the prize, it will be changed to a new state according to the actual situation @Override public boolean raffle() { System.out.println("The lottery is in progress. Please wait a minute!"); Random r = new Random(); int num = r.nextInt(10); // 10% chance of winning if (num == 0) { // Change the activity status to award context activity.setState(activity.getDispenseState()); return true; } else { System.out.println("I'm sorry I didn't win the prize!"); // Change the status to no lucky draw activity.setState(activity.getNoRafflleState()); return false; } } // No prizes can be awarded @Override public void dispensePrize() { System.out.println("If you don't win the prize, you can't give out the prize"); } }
Then there is the specific activity class:
/** * Lucky draw// * * @author Administrator */ public class RaffleActivity { // State indicates the current state of the activity. It is a change State state = null; // Number of prizes int count = 0; // Four attributes representing four states State noRafflleState = new NoRaffleState(this); State canRaffleState = new CanRaffleState(this); State dispenseState = new DispenseState(this); State dispensOutState = new DispenseOutState(this); //constructor //1. Initialize the current state to noRafflleState (i.e. the state that can't draw) //2. Initialize the number of prizes public RaffleActivity(int count) { this.state = getNoRafflleState(); this.count = count; } //Deduct points and call the deductMoney in the current state public void debuctMoney() { state.deductMoney(); } //luck draw public void raffle() { // If the current status is lottery success if (state.raffle()) { //Receive prizes state.dispensePrize(); } } public State getState() { return state; } public void setState(State state) { this.state = state; } //Please note that each time you receive a prize, count-- public int getCount() { int curCount = count; count--; return curCount; } public void setCount(int count) { this.count = count; } public State getNoRafflleState() { return noRafflleState; } public void setNoRafflleState(State noRafflleState) { this.noRafflleState = noRafflleState; } public State getCanRaffleState() { return canRaffleState; } public void setCanRaffleState(State canRaffleState) { this.canRaffleState = canRaffleState; } public State getDispenseState() { return dispenseState; } public void setDispenseState(State dispenseState) { this.dispenseState = dispenseState; } public State getDispensOutState() { return dispensOutState; } public void setDispensOutState(State dispensOutState) { this.dispensOutState = dispensOutState; } }
Client call:
/** * State mode test class * * @author Administrator */ public class ClientTest { public static void main(String[] args) { // TODO Auto-generated method stub // Create an activity object with 1 prize RaffleActivity activity = new RaffleActivity(1); // We drew 300 prizes in a row for (int i = 0; i < 30; i++) { System.out.println("--------The first" + (i + 1) + "Lucky draw----------"); // To participate in the lucky draw, click to deduct points in the first step activity.debuctMoney(); // Step 2 draw activity.raffle(); } } }
The operation results are as follows:
"C:\Program Files (x86)\Java\jdk1.8.0_151\bin\java.exe" "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2020.2.3\lib\idea_rt.jar=51107:C:\Program Files\JetBrains\IntelliJ IDEA 2020.2.3\bin" -Dfile.encoding=GBK -classpath "D:\yingzi\study\Source code\B Station design mode courseware\Source note courseware\code\DesignPattern\bin;C:\Program Files (x86)\Java\jdk1.8.0_151\jre\lib\charsets.jar;C:\Program Files (x86)\Java\jdk1.8.0_151\jre\lib\deploy.jar;C:\Program Files (x86)\Java\jdk1.8.0_151\jre\lib\ext\access-bridge-32.jar;C:\Program Files (x86)\Java\jdk1.8.0_151\jre\lib\ext\cldrdata.jar;C:\Program Files (x86)\Java\jdk1.8.0_151\jre\lib\ext\dnsns.jar;C:\Program Files (x86)\Java\jdk1.8.0_151\jre\lib\ext\jaccess.jar;C:\Program Files (x86)\Java\jdk1.8.0_151\jre\lib\ext\jfxrt.jar;C:\Program Files (x86)\Java\jdk1.8.0_151\jre\lib\ext\localedata.jar;C:\Program Files (x86)\Java\jdk1.8.0_151\jre\lib\ext\nashorn.jar;C:\Program Files (x86)\Java\jdk1.8.0_151\jre\lib\ext\sunec.jar;C:\Program Files (x86)\Java\jdk1.8.0_151\jre\lib\ext\sunjce_provider.jar;C:\Program Files (x86)\Java\jdk1.8.0_151\jre\lib\ext\sunmscapi.jar;C:\Program Files (x86)\Java\jdk1.8.0_151\jre\lib\ext\sunpkcs11.jar;C:\Program Files (x86)\Java\jdk1.8.0_151\jre\lib\ext\zipfs.jar;C:\Program Files (x86)\Java\jdk1.8.0_151\jre\lib\javaws.jar;C:\Program Files (x86)\Java\jdk1.8.0_151\jre\lib\jce.jar;C:\Program Files (x86)\Java\jdk1.8.0_151\jre\lib\jfr.jar;C:\Program Files (x86)\Java\jdk1.8.0_151\jre\lib\jfxswt.jar;C:\Program Files (x86)\Java\jdk1.8.0_151\jre\lib\jsse.jar;C:\Program Files (x86)\Java\jdk1.8.0_151\jre\lib\management-agent.jar;C:\Program Files (x86)\Java\jdk1.8.0_151\jre\lib\plugin.jar;C:\Program Files (x86)\Java\jdk1.8.0_151\jre\lib\resources.jar;C:\Program Files (x86)\Java\jdk1.8.0_151\jre\lib\rt.jar;D:\yingzi\study\Source code\B Station design mode courseware\Source note courseware\code\DesignPattern\src\com\atguigu\proxy\cglib\asm-commons.jar;D:\yingzi\study\Source code\B Station design mode courseware\Source note courseware\code\DesignPattern\src\com\atguigu\proxy\cglib\asm-tree.jar;D:\yingzi\study\Source code\B Station design mode courseware\Source note courseware\code\DesignPattern\src\com\atguigu\proxy\cglib\asm.jar;D:\yingzi\study\Source code\B Station design mode courseware\Source note courseware\code\DesignPattern\src\com\atguigu\proxy\cglib\cglib-2.2.jar" com.atguigu.state.ClientTest --------1st lucky draw---------- After deducting 50 points successfully, you can draw a lottery The lottery is in progress. Please wait a minute! I'm sorry I didn't win the prize! --------Second lucky draw---------- After deducting 50 points successfully, you can draw a lottery The lottery is in progress. Please wait a minute! I'm sorry I didn't win the prize! --------Third lucky draw---------- After deducting 50 points successfully, you can draw a lottery The lottery is in progress. Please wait a minute! I'm sorry I didn't win the prize! --------4th lucky draw---------- After deducting 50 points successfully, you can draw a lottery The lottery is in progress. Please wait a minute! I'm sorry I didn't win the prize! --------Fifth lucky draw---------- After deducting 50 points successfully, you can draw a lottery The lottery is in progress. Please wait a minute! I'm sorry I didn't win the prize! --------Sixth lucky draw---------- After deducting 50 points successfully, you can draw a lottery The lottery is in progress. Please wait a minute! I'm sorry I didn't win the prize! --------7th lucky draw---------- After deducting 50 points successfully, you can draw a lottery The lottery is in progress. Please wait a minute! I'm sorry I didn't win the prize! --------The 8th lucky draw---------- After deducting 50 points successfully, you can draw a lottery The lottery is in progress. Please wait a minute! I'm sorry I didn't win the prize! --------9th lucky draw---------- After deducting 50 points successfully, you can draw a lottery The lottery is in progress. Please wait a minute! I'm sorry I didn't win the prize! --------10th lucky draw---------- After deducting 50 points successfully, you can draw a lottery The lottery is in progress. Please wait a minute! I'm sorry I didn't win the prize! --------11th lucky draw---------- After deducting 50 points successfully, you can draw a lottery The lottery is in progress. Please wait a minute! I'm sorry I didn't win the prize! --------12th lucky draw---------- After deducting 50 points successfully, you can draw a lottery The lottery is in progress. Please wait a minute! I'm sorry I didn't win the prize! --------13th lucky draw---------- After deducting 50 points successfully, you can draw a lottery The lottery is in progress. Please wait a minute! I'm sorry I didn't win the prize! --------14th lucky draw---------- After deducting 50 points successfully, you can draw a lottery The lottery is in progress. Please wait a minute! I'm sorry I didn't win the prize! --------15th lucky draw---------- After deducting 50 points successfully, you can draw a lottery The lottery is in progress. Please wait a minute! I'm sorry I didn't win the prize! --------16th lucky draw---------- After deducting 50 points successfully, you can draw a lottery The lottery is in progress. Please wait a minute! I'm sorry I didn't win the prize! --------17th lucky draw---------- After deducting 50 points successfully, you can draw a lottery The lottery is in progress. Please wait a minute! I'm sorry I didn't win the prize! --------18th lucky draw---------- After deducting 50 points successfully, you can draw a lottery The lottery is in progress. Please wait a minute! I'm sorry I didn't win the prize! --------19th lucky draw---------- After deducting 50 points successfully, you can draw a lottery The lottery is in progress. Please wait a minute! I'm sorry I didn't win the prize! --------The 20th lucky draw---------- After deducting 50 points successfully, you can draw a lottery The lottery is in progress. Please wait a minute! I'm sorry I didn't win the prize! --------21st lucky draw---------- After deducting 50 points successfully, you can draw a lottery The lottery is in progress. Please wait a minute! Congratulations on winning the prize --------22nd lucky draw---------- After deducting 50 points successfully, you can draw a lottery The lottery is in progress. Please wait a minute! I'm sorry I didn't win the prize! --------23rd lucky draw---------- After deducting 50 points successfully, you can draw a lottery The lottery is in progress. Please wait a minute! I'm sorry I didn't win the prize! --------24th lucky draw---------- After deducting 50 points successfully, you can draw a lottery The lottery is in progress. Please wait a minute! I'm sorry I didn't win the prize! --------25th lucky draw---------- After deducting 50 points successfully, you can draw a lottery The lottery is in progress. Please wait a minute! I'm sorry I didn't win the prize! --------26th lucky draw---------- After deducting 50 points successfully, you can draw a lottery The lottery is in progress. Please wait a minute! I'm sorry I didn't win the prize! --------27th lucky draw---------- After deducting 50 points successfully, you can draw a lottery The lottery is in progress. Please wait a minute! I'm sorry I didn't win the prize! --------The 28th lucky draw---------- After deducting 50 points successfully, you can draw a lottery The lottery is in progress. Please wait a minute! I'm sorry I didn't win the prize! --------29th lucky draw---------- After deducting 50 points successfully, you can draw a lottery The lottery is in progress. Please wait a minute! I'm sorry I didn't win the prize! --------30th lucky draw---------- After deducting 50 points successfully, you can draw a lottery The lottery is in progress. Please wait a minute! I'm sorry I didn't win the prize! The process has ended,Exit code 0
2.5 precautions and implementation details of state mode
- The code is very readable. The state pattern encapsulates the behavior of each state into a corresponding class;
- Easy maintenance. Delete the if else statements that are easy to cause problems. If you put the behavior of each state into a class and judge the current state every time you call the method, it will not only produce a lot of if else statements, but also be easy to make mistakes;
- Comply with the "opening and closing principle". Easy to add or delete;
- There are many classes. Each state must have a corresponding class. When there are too many states, many classes will be generated, making maintenance more difficult;
- Application scenario: when an event or object has many states, the States will convert to each other, and different behaviors are required for different states, the state mode can be considered;