Author: Xiao Fu Ge blog: Bugstack.cn – original series of special articles

Precipitation, share, grow, let yourself and others can gain something! 😄

One, foreword

There are three keys to writing good code

If you think of writing code as a soft outfit at home, you will certainly think of the need to have a very good pattern in the home is best north-south transparent, bought back furniture is the best brand to ensure quality, after it is the size is appropriate, can not be placed at the end of the look awkward. So abstracting this process into writing code is three core points; Architecture (the layout of the room), naming (brand and quality), annotation (size specification), only these three points are done well to complete a set of pleasing to the eye of the home.

Plain walk code 🐎 easy to put hard to close

How much code did you write in school? How much code can you write in a year? How much code did you write at home? The technical stack of personal literacy is built brick by brick. The wider and deeper you write, the stronger the foundation. When the foundation is solid, it becomes easy to build on the upper level and easier to build. Often the most difficult is the layer by layer of breakthrough, breakthrough is like breaking the shell, but also like consolidating the foundation, a short time can not see the results, also can not see the height. But later who can go steady, relying on silent precipitation.

The importance of technology inheritance

It may be that the pace of time is too fast now, and a demand comes down and I want to go online on the same day (this demand is very simple, I don’t care how to achieve it, go online tomorrow!) As a result, all the team members were in a panic, anxious, tired and collapsed. Finally, the staff was replaced repeatedly, and the project was handed over for N times in the process, with incomplete documents, chaotic and complicated codes. Whoever took over the project later could only make repairs, just like the unfinished building. This project without inheritance and precipitation is difficult to follow the development of business. In the end! The foundation is weak, the ground is full of chicken feathers.

Second, development environment

  1. JDK 1.8
  2. Idea + Maven
  3. Involving engineering three, can be concerned byThe public,:Bugstack wormhole stackReply,Download the source codeGet (Open the get link and find the serial number 18)
engineering describe
itstack-demo-design-19-00 Scene simulation engineering; Operation services of simulated marketing activities (inquiry and audit)
itstack-demo-design-19-01 Implement business requirements with a lump of code
itstack-demo-design-19-02 Design patterns are optimized to adapt code to generate contrast for learning

3. State mode introduction

State model describes a behavior under various state changes, such as our one of the most common site of page, under the login and don’t you login show content is slightly different (not login cannot display personal information), and the login and login is by changing our state, and make the whole behavior has changed.

It has a row of buttons on top that allow the player to listen to the contents of the tape when it is placed in the slot. Some of the buttons are mutually exclusive. You can only press other buttons when you are in one state (this is also a key point in design mode).

4. Case scenario simulation

In this case, we simulate the marketing activity audit status flow scenario (the launch of an activity is the launch of multiple levels of audit)

It can also be seen from the figure above that our process node includes the associated conditions for each state to reverse to the next state, such as; These state transitions are the scene processing we need to accomplish.

Most programmers have basically developed similar business scenarios, and activities or some configurations need to be reviewed before they can be released to the public. In this review process, multi-level control is often set up with the importance of the system to ensure that an activity can be put online safely and avoid capital loss.

Of course, sometimes some approval flow process configuration is used, which is also very convenient to develop similar processes. You can also set the approval personnel of a node in the configuration. However, this is not our main point. In this case, we mainly simulate the audit control of multiple state nodes of an activity.

1. Scene simulation engineering

itstack-demo-design-19-00└ ─ ─ the SRC └ ─ ─ the main └ ─ ─ Java └ ─ ─ org. Itstack. The demo, the design ├ ─ ─ ActivityInfo. Java ├ ─ ─ Status. The Java └ ─ ─ ActivityService. JavaCopy the code
  • In this simulation project we provide three classes, including; State enumeration (Status), active object (ActivityInfo), Event Services (ActivityService), three service classes.
  • Let’s look at what each of the three classes includes.

2. Code implementation

2.1 Basic activity information

public class ActivityInfo {

    private String activityId;    / / event ID
    private String activityName;  // Activity name
    private Enum<Status> status;  // Active status
    private Date beginTime;       // Start time
    private Date endTime;         // End time
   
    / /... get/set
}  
Copy the code
  • Some basic activity information; Activity ID, activity name, activity status, Start time, end time.

2.2 Activity enumeration state

public enum Status {

    // 1 Create edit, 2 pending review, 3 Approved review (task scanning into the activity), 4 rejected review (can be removed to the editing state), 5 Active, 6 closed activity, 7 Open activity (Task scanning into the activity)
    Editing, Check, Pass, Refuse, Doing, Close, Open

}
Copy the code
  • Enumeration of activities; 1 Create edit, 2 to be reviewed, 3 approved (task scanned into the activity), 4 rejected (can be removed to the editing state), 5 In the activity, 6 closed, 7 open (Task scanned into the activity)

2.3 Active Service Interface

public class ActivityService {

    private static Map<String, Enum<Status>> statusMap = new ConcurrentHashMap<String, Enum<Status>>();

    public static void init(String activityId, Enum<Status> status) {
        // simulate query activity information
        ActivityInfo activityInfo = new ActivityInfo();
        activityInfo.setActivityId(activityId);
        activityInfo.setActivityName("Get up early and learn to Punch in and claim the Prize");
        activityInfo.setStatus(status);
        activityInfo.setBeginTime(new Date());
        activityInfo.setEndTime(new Date());
        statusMap.put(activityId, status);
    }

    /** * query activity information **@paramActivityId activityId *@returnQuery result */
    public static ActivityInfo queryActivityInfo(String activityId) {
        // simulate query activity information
        ActivityInfo activityInfo = new ActivityInfo();
        activityInfo.setActivityId(activityId);
        activityInfo.setActivityName("Get up early and learn to Punch in and claim the Prize");
        activityInfo.setStatus(statusMap.get(activityId));
        activityInfo.setBeginTime(new Date());
        activityInfo.setEndTime(new Date());
        return activityInfo;
    }

    /** * Query activity status **@paramActivityId activityId *@returnQuery result */
    public static Enum<Status> queryActivityStatus(String activityId) {
        return statusMap.get(activityId);
    }

    /** * Execute status change **@paramActivityId activityId *@paramBeforeStatus Status before the change *@paramAfterStatus Status b */
    public static synchronized void execStatus(String activityId, Enum<Status> beforeStatus, Enum<Status> afterStatus) {
        if(! beforeStatus.equals(statusMap.get(activityId)))return; statusMap.put(activityId, afterStatus); }}Copy the code
  • Active query and state change interfaces are provided in this static class;queryActivityInfo,queryActivityStatus,execStatus.
  • The Map structure is used to record the activity ID and state change information, and the init method is used to initialize the activity data. In actual development, this kind of information is basically taken fromThe databaseorRedisIn the acquisition.

Five, with a lump of code implementation

Here we start with the roughest way to implement functionality

The first thing that comes to mind for such state changes is to use if and else for judgment processing. Each state that can flow to the next state can be implemented using nested IF.

1. Engineering structure

itstack-demo-design-19-01└ ─ ─ the SRC └ ─ ─ the main └ ─ ─ Java └ ─ ─ org. Itstack. The demo, the design ├ ─ ─ ActivityExecStatusController. Java └ ─ ─ Result. JavaCopy the code
  • The whole implementation of the engineering structure is relatively simple, including only two classes;ActivityExecStatusController,Result, one handles the process state, and the other is the returned object.

2. Code implementation

public class ActivityExecStatusController {

    /** * active status change * 1. Editing -> Raise, close * 2. Approved -> Rejected, closed, active * 3. Rejected -> Withdrawn, closed * 4. Active -> Close * 5. Active Closed -> Open * 6. Activity on -> Off * *@paramActivityId activityId *@paramBeforeStatus Status before the change *@paramAfterStatus Changed status *@returnReturns the result */
    public Result execStatus(String activityId, Enum<Status> beforeStatus, Enum<Status> afterStatus) {

        // 1. Edit -> raise and close
        if (Status.Editing.equals(beforeStatus)) {
            if (Status.Check.equals(afterStatus) || Status.Close.equals(afterStatus)) {
                ActivityService.execStatus(activityId, beforeStatus, afterStatus);
                return new Result("0000"."Status change successful");
            } else {
                return new Result("0001"."Change status Rejection"); }}// 2. Approved -> Rejected, closed, active
        if (Status.Pass.equals(beforeStatus)) {
            if (Status.Refuse.equals(afterStatus) || Status.Doing.equals(afterStatus) || Status.Close.equals(afterStatus)) {
                ActivityService.execStatus(activityId, beforeStatus, afterStatus);
                return new Result("0000"."Status change successful");
            } else {
                return new Result("0001"."Change status Rejection"); }}// 3. Audit rejection -> withdrawal, closure
        if (Status.Refuse.equals(beforeStatus)) {
            if (Status.Editing.equals(afterStatus) || Status.Close.equals(afterStatus)) {
                ActivityService.execStatus(activityId, beforeStatus, afterStatus);
                return new Result("0000"."Status change successful");
            } else {
                return new Result("0001"."Change status Rejection"); }}// 4. Active -> closed
        if (Status.Doing.equals(beforeStatus)) {
            if (Status.Close.equals(afterStatus)) {
                ActivityService.execStatus(activityId, beforeStatus, afterStatus);
                return new Result("0000"."Status change successful");
            } else {
                return new Result("0001"."Change status Rejection"); }}// 5. Activity closed -> Open
        if (Status.Close.equals(beforeStatus)) {
            if (Status.Open.equals(afterStatus)) {
                ActivityService.execStatus(activityId, beforeStatus, afterStatus);
                return new Result("0000"."Status change successful");
            } else {
                return new Result("0001"."Change status Rejection"); }}// 6. Active start -> Close
        if (Status.Open.equals(beforeStatus)) {
            if (Status.Close.equals(afterStatus)) {
                ActivityService.execStatus(activityId, beforeStatus, afterStatus);
                return new Result("0000"."Status change successful");
            } else {
                return new Result("0001"."Change status Rejection"); }}return new Result("0001"."Unmanageable activity state change"); }}Copy the code
  • Here we just need to look at the structure of the code implementation. It’s a whole piece from top to bottomifelseThis is basically how most beginner programmers develop.
  • Such process-oriented development can still be used where no code changes or second iterations are required (But it's almost impossible not to iterate). And as states and requirements change, it becomes harder to maintain, harder for people to understand and easier to fill in other processes.It's getting messy from the drip to the drip

3. Test and verify

3.1 Writing test classes

@Test
public void test(a) {
    // Initialize the data
    String activityId = "100001";
    ActivityService.init(activityId, Status.Editing);  

    ActivityExecStatusController activityExecStatusController = new ActivityExecStatusController();
    Result resultRefuse = activityExecStatusController.execStatus(activityId, Status.Editing, Status.Refuse); 
    logger.info("Test result (edit To review rejected) : {}", JSON.toJSONString(resultRefuse));                           

    Result resultCheck = activityExecStatusController.execStatus(activityId, Status.Editing, Status.Check);
    logger.info("Test results (in editing To submit for review) : {}", JSON.toJSONString(resultCheck));
}
Copy the code
  • Our test code includes two functional validations, one fromIn the editortoAudit refused toAnd the other is from editing toSubmit audit.
  • As you can see from our scene flow, the activities in editing are not directly accessibleAudit refused toStudent: Yes, there’s something in betweenRemand the case for.

3.2 Test Results

23:24:30.774[the main] INFO org. Itstack. Demo. Design. The test. The ApiTest - test results (refused To audit of the editor) : {"code":"0001"."info":"Change status Rejection"}
23:24:30.778[the main] INFO org. Itstack. Demo. Design. The test. The ApiTest - test results (in the editor To submit audit) : {"code":"0000"."info":"Status change successful"}

Process finished with exit code 0
Copy the code
  • As you can see from the flow of the test results and our status flow, the test results are expected. In addition to being hard to maintain, this development process is fairly fast, but it is not recommended!

State mode refactoring code

The next step is to use state patterns for code optimization, which is a minor refactoring.

Refactoring tends to focus on getting rid of ifelse, which requires interfaces and abstract classes, as well as redesigning the code structure.

1. Engineering structure

itstack-demo-design-19-02├── Java ├─ Java ├─ Java ├─ Java ├─ Java ├─ Java ├─ Java ├─ ├─ ├─ Java ├─ Java ├─ Java ├─ Java ├─ Java ├─ Java ├.java ├ ─ ─ State. Java └ ─ ─ StateHandler. JavaCopy the code

State mode model structure

  • State is an abstract class that defines various operational interfaces (Interrogation, examination, rejection, etc).
  • The different color states on the right are consistent with the colors in our scene simulation, and are the realization operations of various state flow flows. One of the key points of the implementation here is that each state from one state to the next is assigned control by the implementation method, so there is no needifLanguage makes a judgment.
  • Finally,StateHandlerUnified handling of state flows, provided inMapStructure of the various service interface calls, thus avoiding the use ofifDetermine the process of state change.

2. Code implementation

2.1 Define state abstract classes

public abstract class State {

    /** ** activity is questioned **@paramActivityId activityId *@paramCurrentStatus currentStatus *@returnResult */
    public abstract Result arraignment(String activityId, Enum<Status> currentStatus);

    /** * Approved by **@paramActivityId activityId *@paramCurrentStatus currentStatus *@returnResult */
    public abstract Result checkPass(String activityId, Enum<Status> currentStatus);

    /** * Review rejected **@paramActivityId activityId *@paramCurrentStatus currentStatus *@returnResult */
    public abstract Result checkRefuse(String activityId, Enum<Status> currentStatus);

    /** ** ** ** ** ** *@paramActivityId activityId *@paramCurrentStatus currentStatus *@returnResult */
    public abstract Result checkRevoke(String activityId, Enum<Status> currentStatus);

    /** * Activity closed **@paramActivityId activityId *@paramCurrentStatus currentStatus *@returnResult */
    public abstract Result close(String activityId, Enum<Status> currentStatus);

    /** * activity starts **@paramActivityId activityId *@paramCurrentStatus currentStatus *@returnResult */
    public abstract Result open(String activityId, Enum<Status> currentStatus);

    /** * Activities execute **@paramActivityId activityId *@paramCurrentStatus currentStatus *@returnResult */
    public abstract Result doing(String activityId, Enum<Status> currentStatus);

}
Copy the code
  • The interface of various state flow services is provided in the whole interface, for example; There are 7 methods of raising the activity, passing the examination, rejecting the examination and withdrawing the examination.
  • All input parameters are the same in these methods, activityId(Activity ID), the currentStatus (The current state), only their concrete implementation is different.

2.2 Realization of partial state flow

The editor

public class EditingState extends State {

    public Result arraignment(String activityId, Enum<Status> currentStatus) {
        ActivityService.execStatus(activityId, currentStatus, Status.Check);
        return new Result("0000"."The event was successfully proposed.");
    }

    public Result checkPass(String activityId, Enum<Status> currentStatus) {
        return new Result("0001"."Not approved in editing");
    }

    public Result checkRefuse(String activityId, Enum<Status> currentStatus) {
        return new Result("0001"."Unmoderable reject in editing");
    }

    @Override
    public Result checkRevoke(String activityId, Enum<Status> currentStatus) {
        return new Result("0001"."Irrevocable audit in editing");
    }

    public Result close(String activityId, Enum<Status> currentStatus) {
        ActivityService.execStatus(activityId, currentStatus, Status.Close);
        return new Result("0000"."Activity closed successfully");
    }

    public Result open(String activityId, Enum<Status> currentStatus) {
        return new Result("0001"."Cannot be opened for non-closed activities");
    }

    public Result doing(String activityId, Enum<Status> currentStatus) {
        return new Result("0001"."The activity in editing cannot execute the change in the activity"); }}Copy the code

Remand the case for

public class CheckState extends State {

    public Result arraignment(String activityId, Enum<Status> currentStatus) {
        return new Result("0001"."Pending review cannot be repeated.");
    }

    public Result checkPass(String activityId, Enum<Status> currentStatus) {
        ActivityService.execStatus(activityId, currentStatus, Status.Pass);
        return new Result("0000"."Activity audit has been approved.");
    }

    public Result checkRefuse(String activityId, Enum<Status> currentStatus) {
        ActivityService.execStatus(activityId, currentStatus, Status.Refuse);
        return new Result("0000"."Activity audit refused to complete.");
    }

    @Override
    public Result checkRevoke(String activityId, Enum<Status> currentStatus) {
        ActivityService.execStatus(activityId, currentStatus, Status.Editing);
        return new Result("0000"."Activity audit withdrawn back to edit");
    }

    public Result close(String activityId, Enum<Status> currentStatus) {
        ActivityService.execStatus(activityId, currentStatus, Status.Close);
        return new Result("0000"."Activity audit shutdown completed.");
    }

    public Result open(String activityId, Enum<Status> currentStatus) {
        return new Result("0001"."Cannot be opened for non-closed activities");
    }

    public Result doing(String activityId, Enum<Status> currentStatus) {
        return new Result("0001"."Change in activity not available for audit"); }}Copy the code
  • The contents of two concrete implementation classes are provided here, edit state and arraign state.
  • For example, in these two implementation classes,checkRefuseThis method has different implementations for different classes, that is, the next flow operation that can be done in different states can be controlled in each method.
  • The operations of the other five classes are similar and will not be demonstrated here. Most of them are repetitive code. Can be learned through the source code.

2.3 Status processing service

public class StateHandler {

    private Map<Enum<Status>, State> stateMap = new ConcurrentHashMap<Enum<Status>, State>();

    public StateHandler(a) {
        stateMap.put(Status.Check, new CheckState());     / / approval
        stateMap.put(Status.Close, new CloseState());     / / has been closed
        stateMap.put(Status.Doing, new DoingState());     / / activity
        stateMap.put(Status.Editing, new EditingState()); / / edit
        stateMap.put(Status.Open, new OpenState());       / / has been open
        stateMap.put(Status.Pass, new PassState());       // Approved
        stateMap.put(Status.Refuse, new RefuseState());   // Review rejection
    }

    public Result arraignment(String activityId, Enum<Status> currentStatus) {
        return stateMap.get(currentStatus).arraignment(activityId, currentStatus);
    }

    public Result checkPass(String activityId, Enum<Status> currentStatus) {
        return stateMap.get(currentStatus).checkPass(activityId, currentStatus);
    }

    public Result checkRefuse(String activityId, Enum<Status> currentStatus) {
        return stateMap.get(currentStatus).checkRefuse(activityId, currentStatus);
    }

    public Result checkRevoke(String activityId, Enum<Status> currentStatus) {
        return stateMap.get(currentStatus).checkRevoke(activityId, currentStatus);
    }

    public Result close(String activityId, Enum<Status> currentStatus) {
        return stateMap.get(currentStatus).close(activityId, currentStatus);
    }

    public Result open(String activityId, Enum<Status> currentStatus) {
        return stateMap.get(currentStatus).open(activityId, currentStatus);
    }

    public Result doing(String activityId, Enum<Status> currentStatus) {
        returnstateMap.get(currentStatus).doing(activityId, currentStatus); }}Copy the code
  • This is the unified control center for the state service, and you can see that the concrete associations of all states and implementations are provided in the constructor, into the Map data structure.
  • At the same time provides different names of the interface operation class, so that external callers can more easily use the functional interface, without the need to like initstack-demo-design-19-01In this example, you have to pass two states.

3. Test and verify

3.1 Writing test Classes (Editing2Arraignment)

@Test
public void test_Editing2Arraignment(a) {
    String activityId = "100001";
    ActivityService.init(activityId, Status.Editing);
    StateHandler stateHandler = new StateHandler();
    Result result = stateHandler.arraignment(activityId, Status.Editing);
    logger.info("Test result (editing To review activity) : {}", JSON.toJSONString(result));
    logger.info("Activity info: {} Status: {}", JSON.toJSONString(ActivityService.queryActivityInfo(activityId)), JSON.toJSONString(ActivityService.queryActivityInfo(activityId).getStatus()));
}
Copy the code

The test results

23:59:20.883[the main] INFO org. Itstack. Demo. Design. The test. The ApiTest - test results To remand the case for activities in (edit) : {"code":"0000"."info":"The event was successfully proposed."}
23:59:20.907[the main] INFO org. Itstack. Demo. Design. The test. The ApiTest - activity information: {"activityId":"100001"."activityName":"Get up early and learn to Punch in and claim the Prize"."beginTime":1593694760892."endTime":1593694760892."status":"Check"} state:"Check"

Process finished with exit code 0
Copy the code
  • Test the state flow of the To review activity in editing.

3.2 Writing a Test Class (Editing2Open)

@Test
public void test_Editing2Open(a) {
    String activityId = "100001";
    ActivityService.init(activityId, Status.Editing);
    StateHandler stateHandler = new StateHandler();
    Result result = stateHandler.open(activityId, Status.Editing);
    logger.info("Test result (To open activity in editing) : {}", JSON.toJSONString(result));
    logger.info("Activity info: {} Status: {}", JSON.toJSONString(ActivityService.queryActivityInfo(activityId)), JSON.toJSONString(ActivityService.queryActivityInfo(activityId).getStatus()));
}
Copy the code

The test results

23:59:36.904[the main] INFO org. Itstack. Demo. Design. The test. The ApiTest - test results (activities) in the editor To open: {"code":"0001"."info":"Cannot be opened for non-closed activities"}
23:59:36.914[the main] INFO org. Itstack. Demo. Design. The test. The ApiTest - activity information: {"activityId":"100001"."activityName":"Get up early and learn to Punch in and claim the Prize"."beginTime":1593694776907."endTime":1593694776907."status":"Editing"} state:"Editing"

Process finished with exit code 0
Copy the code
  • Test the state flow of the To open activity in editing.

3.3 Writing test Classes (Refuse2Doing)

@Test
public void test_Refuse2Doing(a) {
    String activityId = "100001";
    ActivityService.init(activityId, Status.Refuse);
    StateHandler stateHandler = new StateHandler();
    Result result = stateHandler.doing(activityId, Status.Refuse);
    logger.info("Test result (reject To activity) : {}", JSON.toJSONString(result));
    logger.info("Activity info: {} Status: {}", JSON.toJSONString(ActivityService.queryActivityInfo(activityId)), JSON.toJSONString(ActivityService.queryActivityInfo(activityId).getStatus()));
}
Copy the code

The test results

23:59:46.339[the main] INFO org. Itstack. Demo. Design. The test. The ApiTest - test results (refused To activity) : {"code":"0001"."info":"Audit rejected unexecutable activities in progress"}
23:59:46.352[the main] INFO org. Itstack. Demo. Design. The test. The ApiTest - activity information: {"activityId":"100001"."activityName":"Get up early and learn to Punch in and claim the Prize"."beginTime":1593694786342."endTime":1593694786342."status":"Refuse"} state:"Refuse"

Process finished with exit code 0
Copy the code
  • Tests state flow in the Reject To activity.

3.4 Writing test class (Refuse2Revoke)

@Test
public void test_Refuse2Revoke(a) {
    String activityId = "100001";
    ActivityService.init(activityId, Status.Refuse);
    StateHandler stateHandler = new StateHandler();
    Result result = stateHandler.checkRevoke(activityId, Status.Refuse);
    logger.info("Test result (refused To withdraw) : {}", JSON.toJSONString(result));
    logger.info("Activity info: {} Status: {}", JSON.toJSONString(ActivityService.queryActivityInfo(activityId)), JSON.toJSONString(ActivityService.queryActivityInfo(activityId).getStatus()));
}
Copy the code

The test results

23:59:50.197[the main] INFO org. Itstack. Demo. Design. The test. The ApiTest - test results (refused To withdraw trial) : {"code":"0000"."info":"Audit withdrawal completed"}
23:59:50.208[the main] INFO org. Itstack. Demo. Design. The test. The ApiTest - activity information: {"activityId":"100001"."activityName":"Get up early and learn to Punch in and claim the Prize"."beginTime":1593694810201."endTime":1593694810201."status":"Editing"} state:"Editing"

Process finished with exit code 0
Copy the code
  • Test test results (reject To withdraw), state flow.

  • To sum up, the above four test classes respectively simulate effective flow and rejected flow between different states, and different state services deal with different service contents.

Seven,

  • As you can see from the implementation of a requirement in the above two ways, it is gone after the second design pattern is usedifelseThe structure of the code is also clearer and easier to expand. This is the beauty of design patterns, which can change the structure of existing code in a very powerful way, making it easier to extend and maintain later.
  • You can see in the way the implementation structure is coded that this is no longer procedural programming, but object oriented architecture. And this design pattern is satisfiedSingle responsibilityandThe open closed principleIt’s easy to extend code if you have a structure that doesn’t affect the overall change.
  • But if there are more states and flows, as in the case of this article, there will be more implementation classes. There may also be a time cost to the implementation of the code, since the return on investment can be evaluated on demand if such a scenario is encountered. The main point is whether it can be modified frequently, whether it can be componentized, and whether it can be separated from business and non-business functions.

Recommended reading

  • 1. Relearn Java design mode: Actual factory method mode "Different interfaces of various types of goods, unified prize awarding service setting scene"
  • 2. Relearn Java design mode: actual combat prototype mode "computer test multiple sets of tests, each question and answer out of order scene"
  • 3. Relearn Java design mode: practice bridge mode "Multi-payment channel (wechat, Alipay) and multi-payment mode (face brushing, fingerprint) scenario"
  • 4. Re-learn Java design mode: practical combination mode "issuing coupons to differentiated groups of marketing and setting up scenes of decision tree engine"
  • 5. Relearn Java design mode: actual combat appearance mode "Develop facade mode middleware based on SpringBoot, unified control interface whitelist scenario"
  • 6. Re-learn Java design mode: actual combat Mode "Redis based on second kill, provide activity and inventory information query scenarios"
  • 7. Re-learn Java design mode: Actual combat memo mode "Simulate the configuration file rollback scenario in the online Process of the Internet system"