Blog: bugstack.cn
Precipitation, share, grow, let yourself and others can gain something! 😄
One, foreword
Multiple-choice tests are for kids, but I want them for adults
For the first few years, the group would just ask me which development language to learn and which was the best. Some people support PHP, others call Java, C++ and C#. But in recent years, it seems that people don’t really talk about knives, guns, sticks, tomahawk, hook and fork. Most of the time, it’s just a joke. At the same time, in the overall Development of the Internet, a lot of times, some development languages are common, together to create an overall ecosystem. And the way people choose is more inclined to choose suitable architecture in different fields, rather than blindly pursuing a certain language. This can give a lot of beginners programming some advice, do not deliberately feel that a language is good, a language is not good, just in the right scenario to choose the most needed. The language you want to choose can be determined by reference to the demand and salary level of job websites.
Programming is not showmanship
There will always be people who like to add new features to their overall project development and put their new knowledge to the test. It can’t be said that this is not good, even it can be said that part of the people who love learning, like innovation, like practice. But programming in addition to using new features, but also need to consider the overall scalability, readability, maintenance, easy to expand and other considerations. Just as you have a team of decorators in your house, there is a small worker who likes to show off his flower work and installs a toilet under the shower 🚿 at home 🚽.
Even writing CRUD should have design patterns
Often a lot of big requirements are piled out by adding, deleting, changing and checking. Today we need a requirement if, and tomorrow we need to add else content to expand it. Over time, the demand is increasing, and the cost of expansion and maintenance is increasing. Often most of the RESEARCH and development is not product thinking and overall business requirements oriented, always think that write good code to complete the function. However, such an implementation without consideration of extensibility makes it difficult for subsequent requirements to iterate quickly, and over time, they will be trapped in a vicious cycle with bugs to be fixed every day.
Second, development environment
- JDK 1.8
- Idea + Maven
- Involving engineering three, can be concerned byThe public,:
Bugstack wormhole stack
Reply,Download the source code
Get (Open the get link and find the serial number 18)
engineering | describe |
---|---|
itstack-demo-design-8-01 | Implement business requirements with a lump of code |
itstack-demo-design-8-02 | Design patterns are optimized to adapt code to generate contrast for learning |
Iii. Introduction of combination mode
As you can see from the picture above, this is a bit like screws 🔩 and nuts, organized into a structure tree by a bunch of links. The idea of combining similar objects (also known as methods) into a set of structure-tree objects that can be called is called the composition pattern.
This design allows your service group nodes to freely combine to provide external services. For example, you have three atom check functions (A: ID card, B: bank card, C: mobile phone number) and provide external call and use. Some callers need to use the AB combination, some need to use the CBA combination, and some may use only one of the three. At this point you can use the composite pattern to build the service, configure different organizational tree for different types of callers, and this tree structure can be configured into the database and can be constantly controlled through the graphical interface.
So the different design patterns used in the right scenarios can make the code logic very clear and easy to expand, while also reducing the cost of learning the project for new people on the team.
4. Case scenario simulation
The above is a very simplified version of the decision tree of marketing rules, which issues different types of coupons according to gender and age, so as to stimulate consumption and achieve the purpose of promoting users’ life accurately.
While some of you may not have developed marketing scenarios, you may be being marketed all the time. For example, if you regularly browse for mechanical keyboards, laptops, car decorations, etc., which men like, then recommend these coupons for you to stimulate your spending. So if you’re not shopping enough, or you don’t have the money. Have you ever taken a taxi? For a period of time, your friend often shouted, “Why does he get 10 yuan for the same distance, and I get 15 yuan?” In fact, these are marketing cases, generally for those who do not often use software partners, often carry out slightly greater efforts to promote activity, increase user stickiness.
So here we simulate a similar decision scenario, reflecting the importance of the combination pattern in it. In addition, the combination mode can not only be applied to rule decision tree, but also can be used as service packaging to configure different interfaces in combination, provide external service capabilities and reduce development costs.
Five, with a lump of code implementation
Here we take an example of the birth of ifElse, introducing the accident caused by the story between little sister and the programmer 👨💻.
The date of | demand | The degree of emergency | Programmer (VOICE) |
---|---|---|---|
Monday morning | Ape brother, the boss said to do some marketing pull quantity, give boys and girls to send different coupons, promote consumption. | It’s urgent. I need it after work | Okay, it’s not that hard. Add a judgment and go online |
Tuesday afternoon | Dude, we’ve been great since we got online. Let us according to the young, middle-aged, adult, different age plus judgment, accurate stimulation of consumption. | It’s super urgent. I need it tomorrow | It’s not hard. Just add it |
Wednesday evening | Hey, little brother! To sleep! The boss said that our activity was very successful, can we subdivide the single, married and children into different judgments? This is more incentive for users to spend. | Thief emergency, online asap. | Be aware ofifelse More and more |
Thursday. Early morning | A: wow! You guys are so good. You got there so fast. Hee hee! I have a small request, we need to adjust the age group, because now the students of the Student Affairs Office are early, it is easier for those who have a date to buy something. Change the value! Hard work! | Boss, waiting! | A lot of values to change, oops! So muchifelse the |
Friday. Midnight | Say hello! Baba, broken, how to send the wrong coupon, a customer complained, a lot of girls to complain. Look at that. The boss, he… | (a sweat), ah, the value of the wrong position! | After all, a person carried all |
1. Engineering structure
itstack-demo-design-8-01└── Java sci-press ─ org.sci-impCopy the code
- If the company is full of such programmers, it will definitely save a lot of costs. Don’t build microservices at all. One project can handle all the business!
- But don’t do it!
Meat and wine wear intestines, Buddha left in the heart. If the world learns from me, it will be like entering a demon path.
2. Code implementation
public class EngineController {
private Logger logger = LoggerFactory.getLogger(EngineController.class);
public String process(final String userId, final String userSex, final int userAge) {
logger.info("The ifelse implementation determines user results. UserId: {} userSex: {} userAge: {}", userId, userSex, userAge);
if ("man".equals(userSex)) {
if (userAge < 25) {
return Fruit A;
}
if (userAge >= 25) {
return Fruit "B"; }}if ("woman".equals(userSex)) {
if (userAge < 25) {
return The fruits "C";
}
if (userAge >= 25) {
return The fruits "D"; }}return null; }}Copy the code
- Aside from the extensibility and maintenance we talked about, this code is the fastest to implement. And from the look of it is also suitable for newcomers to understand.
- but
I advise you not to write
You’ll either get your performance docked or you’ll get fired.
3. Test and verify
3.1 Writing test classes
@Test
public void test_EngineController(a) {
EngineController engineController = new EngineController();
String process = engineController.process("Oli09pLkdjh"."man".29);
logger.info("Test result: {}", process);
}
Copy the code
- Here we simulate a user ID and transfer gender: man, age: 29. Our expected result is: fruit B. The actual corresponding business is to give
The bald programmer hands out a goji berry coupon
.
3.2 Test Results
22:10:12.891[the main] INFO O.I.D emo. Design. EngineController - ifelse implementation judgment results by the user. UserId: Oli09pLkdjh userSex: man userAge:29
22:10:12.898[the main] INFO org. Itstack. Demo. Design. The test. The ApiTest - test results: fruit B Process finished with exit code0
Copy the code
- The test results show that our program works well and meets expectations, but the implementation is not what we recommend. And then we’re going to use
Portfolio model
To optimize this part of the code.
Refactoring code in combination mode
The code is then optimized using the composite pattern, which is a minor refactoring.
The reconstruction of the next part of the code to change the momentum will be relatively larger, in order to we can use different types of decision node and the fruits of the final assembly into a decision tree can be run in, you need to do to fit design and factory method calls, specific will be reflected in defining interfaces and abstract classes and initialization configuration decision node (gender, age). It is recommended to read this part of the code several times, as a best practice.
1. Engineering structure
itstack-demo-design-8-02└ ─ ─ the SRC ├ ─ ─ the main │ └ ─ ─ Java │ └ ─ ─ org. Itstack. Demo. Design. The domain │ ├ ─ ─ model │ │ ├ ─ ─ aggregates │ │ │ └ ─ ─ TreeRich. Java │ │ └ ─ ─ vo │ │ ├ ─ ─ EngineResult. Java │ │ ├ ─ ─ TreeNode. Java │ │ ├ ─ ─ TreeNodeLink. Java │ │ └ ─ ─ TreeRoot. Java │ └ ─ ─ the service │ ├ ─ ─ engine │ │ ├ ─ ─ impl │ │ │ └ ─ ─ TreeEngineHandle. Java │ │ ├ ─ ─ EngineBase. Java │ │ ├ ─ ─ EngineConfig. Java │ │ └ ─ ─ IEngine. Java │ └ ─ ─ logic │ ├ ─ ─ impl │ │ ├ ─ ─ LogicFilter. Java │ │ └ ─ ─ LogicFilter. Java │ └ ─ ─ LogicFilter. Java └ ─ ─ the test └ ─ ─ Java └ ─ ─ org. Itstack. The demo, the design, test └ ─ ─ ApiTest. JavaCopy the code
Composite pattern model structure
-
First, take a look at the simulation guidance tree structure in the black box. 1, 11, 12, 111, 112, 121, 122, these are the ids of a group of tree structures, and a relational tree is formed by connecting nodes in series.
-
Next comes the class diagram section. On the left, the adaptive decision filters are defined starting with LogicFilter. BaseLogic is an implementation of the interface that provides the most basic generic method. UserAgeFilter and UserGenerFilter are two concrete implementation classes used to determine age and gender.
-
And then finally, the engine that executes on this decision tree that can be organized. The engine interface and basic configuration are also defined, in which the required pattern decision nodes are set up.
-
static { logicFilterMap = new ConcurrentHashMap<>(); logicFilterMap.put("userAge".new UserAgeFilter()); logicFilterMap.put("userGender".new UserGenderFilter()); } Copy the code
-
-
Next, I will explain each class in detail. If YOU don’t understand it, it must be because my author’s expression is not clear enough. You can add my wechat (Fustack) to communicate with me.
2. Code implementation
2.1 Basic Objects
Package path | class | introduce |
---|---|---|
model.aggregates | TreeRich | Aggregate object that contains organization tree information |
model.vo | EngineResult | The decision returns object information |
model.vo | TreeNode | Tree node; Cotyledon node, fruit node |
model.vo | TreeNodeLink | Tree nodes link links |
model.vo | TreeRoot | The root of information |
- The above section is a brief introduction, which contains no logic and only necessary attributes
get/set
, the entire source code can be followed by wechat public number:Bugstack wormhole stack
, reply source download open link to obtain.
2.2 Logical filter interface of tree Node
public interface LogicFilter {
/** * logical decision **@paramMatterValue Decision value *@paramTreeno suspect infolist decision nodes *@returnId of the next node */
Long filter(String matterValue, List<TreeNodeLink> treeNodeLineInfoList);
/** * get the decision value **@paramDecisionMatter material *@returnDecision-making value * /
String matterValue(Long treeId, String userId, Map<String, String> decisionMatter);
}
Copy the code
- This part defines the adaptive general interface, logical decision maker, obtain decision value, so that every node providing decision ability must implement this interface, to ensure the unity.
2.3 Decision abstract classes provide basic services
public abstract class BaseLogic implements LogicFilter {
@Override
public Long filter(String matterValue, List<TreeNodeLink> treeNodeLinkList) {
for (TreeNodeLink nodeLine : treeNodeLinkList) {
if (decisionLogic(matterValue, nodeLine)) return nodeLine.getNodeIdTo();
}
return 0L;
}
@Override
public abstract String matterValue(Long treeId, String userId, Map<String, String> decisionMatter);
private boolean decisionLogic(String matterValue, TreeNodeLink nodeLink) {
switch (nodeLink.getRuleLimitType()) {
case 1:
return matterValue.equals(nodeLink.getRuleLimitValue());
case 2:
return Double.parseDouble(matterValue) > Double.parseDouble(nodeLink.getRuleLimitValue());
case 3:
return Double.parseDouble(matterValue) < Double.parseDouble(nodeLink.getRuleLimitValue());
case 4:
return Double.parseDouble(matterValue) <= Double.parseDouble(nodeLink.getRuleLimitValue());
case 5:
return Double.parseDouble(matterValue) >= Double.parseDouble(nodeLink.getRuleLimitValue());
default:
return false; }}}Copy the code
- The interface method is implemented in abstract method, and the basic decision method is defined.
One, two, three, four, five
.Equal to, less than, greater than, less than or equal to, greater than or equal to
The judgment logic of. - Abstract methods are also defined so that every class that implements an interface must be provided in accordance with the rules
Decision-making value
, this decision value is used for logical comparison.
2.4 Tree node logic implementation class
Age node
public class UserAgeFilter extends BaseLogic {
@Override
public String matterValue(Long treeId, String userId, Map<String, String> decisionMatter) {
return decisionMatter.get("age"); }}Copy the code
Gender node
public class UserGenderFilter extends BaseLogic {
@Override
public String matterValue(Long treeId, String userId, Map<String, String> decisionMatter) {
return decisionMatter.get("gender"); }}Copy the code
- The above two decision logic nodes are very simple to get the value, just get the user’s input. The actual business development can be obtained from databases, RPC interfaces, cache operations, etc.
2.5 Decision Engine Interface Definition
public interface IEngine {
EngineResult process(final Long treeId, final String userId, TreeRich treeRich, final Map<String, String> decisionMatter);
}
Copy the code
- Users also need to define unified interface operation, which is very convenient for the subsequent expansion of different types of decision engines, that is, to build different decision factories.
2.6 Decision Node Configuration
public class EngineConfig {
static Map<String, LogicFilter> logicFilterMap;
static {
logicFilterMap = new ConcurrentHashMap<>();
logicFilterMap.put("userAge".new UserAgeFilter());
logicFilterMap.put("userGender".new UserGenderFilter());
}
public Map<String, LogicFilter> getLogicFilterMap(a) {
return logicFilterMap;
}
public void setLogicFilterMap(Map<String, LogicFilter> logicFilterMap) {
this.logicFilterMap = logicFilterMap; }}Copy the code
- This is where the decision nodes that can provide services are configured to
map
In the structure, for something like thismap
The structure can be extracted into the database, which makes it very easy to manage.
2.7 Basic decision engine functions
public abstract class EngineBase extends EngineConfig implements IEngine {
private Logger logger = LoggerFactory.getLogger(EngineBase.class);
@Override
public abstract EngineResult process(Long treeId, String userId, TreeRich treeRich, Map<String, String> decisionMatter);
protected TreeNode engineDecisionMaker(TreeRich treeRich, Long treeId, String userId, Map<String, String> decisionMatter) {
TreeRoot treeRoot = treeRich.getTreeRoot();
Map<Long, TreeNode> treeNodeMap = treeRich.getTreeNodeMap();
// Rule root ID
Long rootNodeId = treeRoot.getTreeRootNodeId();
TreeNode treeNodeInfo = treeNodeMap.get(rootNodeId);
// NodeType [NodeType]; 1 cotyledon, 2 fruit
while (treeNodeInfo.getNodeType().equals(1)) {
String ruleKey = treeNodeInfo.getRuleKey();
LogicFilter logicFilter = logicFilterMap.get(ruleKey);
String matterValue = logicFilter.matterValue(treeId, userId, decisionMatter);
Long nextNode = logicFilter.filter(matterValue, treeNodeInfo.getTreeNodeLinkList());
treeNodeInfo = treeNodeMap.get(nextNode);
logger.info("Decision tree engine =>{} userId: {} treeId: {} treeNode: {} ruleKey: {} matterValue: {}", treeRoot.getTreeName(), userId, treeId, treeNodeInfo.getTreeNodeId(), ruleKey, matterValue);
}
returntreeNodeInfo; }}Copy the code
- The processing process of decision tree flow is mainly provided here, which is similar to the relationship through links (
gender
,age
) the process of finding fruit nodes in a binary tree. - At the same time, it provides an abstract method to execute the decision-making process for external implementation.
2.8 Implementation of decision engine
public class TreeEngineHandle extends EngineBase {
@Override
public EngineResult process(Long treeId, String userId, TreeRich treeRich, Map<String, String> decisionMatter) {
// The decision process
TreeNode treeNode = engineDecisionMaker(treeRich, treeId, userId, decisionMatter);
// Decision result
return newEngineResult(userId, treeId, treeNode.getTreeNodeId(), treeNode.getNodeValue()); }}Copy the code
- Here the implementation of the decision engine is very simple, by passing in the necessary information; Decision tree information, decision material value, to make specific tree structure decision.
3. Test and verify
3.1 Assembly tree relationship
@Before
public void init(a) {
// Node: 1
TreeNode treeNode_01 = new TreeNode();
treeNode_01.setTreeId(10001L);
treeNode_01.setTreeNodeId(1L);
treeNode_01.setNodeType(1);
treeNode_01.setNodeValue(null);
treeNode_01.setRuleKey("userGender");
treeNode_01.setRuleDesc("User gender [male/female]");
// Link: 1->11
TreeNodeLink treeNodeLink_11 = new TreeNodeLink();
treeNodeLink_11.setNodeIdFrom(1L);
treeNodeLink_11.setNodeIdTo(11L);
treeNodeLink_11.setRuleLimitType(1);
treeNodeLink_11.setRuleLimitValue("man");
// Link: 1->12
TreeNodeLink treeNodeLink_12 = new TreeNodeLink();
treeNodeLink_12.setNodeIdTo(1L);
treeNodeLink_12.setNodeIdTo(12L);
treeNodeLink_12.setRuleLimitType(1);
treeNodeLink_12.setRuleLimitValue("woman");
List<TreeNodeLink> treeNodeLinkList_1 = new ArrayList<>();
treeNodeLinkList_1.add(treeNodeLink_11);
treeNodeLinkList_1.add(treeNodeLink_12);
treeNode_01.setTreeNodeLinkList(treeNodeLinkList_1);
// Node: 11
TreeNode treeNode_11 = new TreeNode();
treeNode_11.setTreeId(10001L);
treeNode_11.setTreeNodeId(11L);
treeNode_11.setNodeType(1);
treeNode_11.setNodeValue(null);
treeNode_11.setRuleKey("userAge");
treeNode_11.setRuleDesc("User age");
// Link: 11->111
TreeNodeLink treeNodeLink_111 = new TreeNodeLink();
treeNodeLink_111.setNodeIdFrom(11L);
treeNodeLink_111.setNodeIdTo(111L);
treeNodeLink_111.setRuleLimitType(3);
treeNodeLink_111.setRuleLimitValue("25");
// Link: 11->112
TreeNodeLink treeNodeLink_112 = new TreeNodeLink();
treeNodeLink_112.setNodeIdFrom(11L);
treeNodeLink_112.setNodeIdTo(112L);
treeNodeLink_112.setRuleLimitType(5);
treeNodeLink_112.setRuleLimitValue("25");
List<TreeNodeLink> treeNodeLinkList_11 = new ArrayList<>();
treeNodeLinkList_11.add(treeNodeLink_111);
treeNodeLinkList_11.add(treeNodeLink_112);
treeNode_11.setTreeNodeLinkList(treeNodeLinkList_11);
// Node: 12
TreeNode treeNode_12 = new TreeNode();
treeNode_12.setTreeId(10001L);
treeNode_12.setTreeNodeId(12L);
treeNode_12.setNodeType(1);
treeNode_12.setNodeValue(null);
treeNode_12.setRuleKey("userAge");
treeNode_12.setRuleDesc("User age");
// Link: 12->121
TreeNodeLink treeNodeLink_121 = new TreeNodeLink();
treeNodeLink_121.setNodeIdFrom(12L);
treeNodeLink_121.setNodeIdTo(121L);
treeNodeLink_121.setRuleLimitType(3);
treeNodeLink_121.setRuleLimitValue("25");
// Link: 12->122
TreeNodeLink treeNodeLink_122 = new TreeNodeLink();
treeNodeLink_122.setNodeIdFrom(12L);
treeNodeLink_122.setNodeIdTo(122L);
treeNodeLink_122.setRuleLimitType(5);
treeNodeLink_122.setRuleLimitValue("25");
List<TreeNodeLink> treeNodeLinkList_12 = new ArrayList<>();
treeNodeLinkList_12.add(treeNodeLink_121);
treeNodeLinkList_12.add(treeNodeLink_122);
treeNode_12.setTreeNodeLinkList(treeNodeLinkList_12);
// Node: 111
TreeNode treeNode_111 = new TreeNode();
treeNode_111.setTreeId(10001L);
treeNode_111.setTreeNodeId(111L);
treeNode_111.setNodeType(2);
treeNode_111.setNodeValue(Fruit A);
// Node: 112
TreeNode treeNode_112 = new TreeNode();
treeNode_112.setTreeId(10001L);
treeNode_112.setTreeNodeId(112L);
treeNode_112.setNodeType(2);
treeNode_112.setNodeValue(Fruit "B");
// Node: 121
TreeNode treeNode_121 = new TreeNode();
treeNode_121.setTreeId(10001L);
treeNode_121.setTreeNodeId(121L);
treeNode_121.setNodeType(2);
treeNode_121.setNodeValue(The fruits "C");
// Node: 122
TreeNode treeNode_122 = new TreeNode();
treeNode_122.setTreeId(10001L);
treeNode_122.setTreeNodeId(122L);
treeNode_122.setNodeType(2);
treeNode_122.setNodeValue(The fruits "D");
/ / the roots
TreeRoot treeRoot = new TreeRoot();
treeRoot.setTreeId(10001L);
treeRoot.setTreeRootNodeId(1L);
treeRoot.setTreeName("Rule decision tree");
Map<Long, TreeNode> treeNodeMap = new HashMap<>();
treeNodeMap.put(1L, treeNode_01);
treeNodeMap.put(11L, treeNode_11);
treeNodeMap.put(12L, treeNode_12);
treeNodeMap.put(111L, treeNode_111);
treeNodeMap.put(112L, treeNode_112);
treeNodeMap.put(121L, treeNode_121);
treeNodeMap.put(122L, treeNode_122);
treeRich = new TreeRich(treeRoot, treeNodeMap);
}
Copy the code
- Important, this part is the very important use of the combination mode. Under the decision tree relationship we have built, we can create each node of the tree and connect the nodes in series by using links.
- In time, you can add corresponding nodes and dynamic configuration to any business expansion you need.
- This part of the manual combination of the way can be extracted into the database, so it can be extended to the graphical interface for configuration operations.
3.2 Writing test classes
@Test
public void test_tree(a) {
logger.info("Decision tree combinatorial structure information: \r\n" + JSON.toJSONString(treeRich));
IEngine treeEngineHandle = new TreeEngineHandle();
Map<String, String> decisionMatter = new HashMap<>();
decisionMatter.put("gender"."man");
decisionMatter.put("age"."29");
EngineResult result = treeEngineHandle.process(10001L."Oli09pLkdjh", treeRich, decisionMatter);
logger.info("Test result: {}", JSON.toJSONString(result));
}
Copy the code
- The process decision tree created by the organization mode is provided here, and the ID of the decision tree is passed in the call. Then, if it is business development, the binding relationship between the decision tree and business can be easily decoupled, and the ID of the decision tree can be passed as needed.
- In addition to the input we also provide need to deal with;
male
(man),age
(29 years old).
3.3 Test Results
23:35:05.711[the main] INFO O.I.D.D.D.S ervice. Engine. EngineBase - the decision tree rule engine = > decision tree userId: Oli09pLkdjh treeId:10001TreeNode:11RuleKey: userGender matterValue: man23:35:05.712[the main] INFO O.I.D.D.D.S ervice. Engine. EngineBase - the decision tree rule engine = > decision tree userId: Oli09pLkdjh treeId:10001TreeNode:112RuleKey: userAge matterValue:29
23:35:05.715[the main] INFO org. Itstack. Demo. Design. The test. The ApiTest - test results: {"nodeId":112."nodeValue":Fruit "B"."success":true."treeId":10001."userId":"Oli09pLkdjh"}
Process finished with exit code 0
Copy the code
- According to the test results, this is consistent with our use
ifelse
It is the same, but under the current combined mode design, it is very convenient for subsequent expansion and modification. - The overall organizational relationship framework and call decision flow has been set up. If you don’t fully understand this, download the code to observe the structure and run debugging.
Seven,
- From the above decision tree scenario, the combined mode mainly solves a series of simple logical nodes or extended complex logical nodes. Under the organization of different structures, external calls can still be very simple.
- This part of the design pattern ensures that the open closed principle allows you to provide the use of new logical nodes and organize new tree relationships without changing the structure of the model. However, it will be more difficult to package some interfaces with very different functions, but it is not impossible to handle, but it needs to do some adaptation and specific development.
- A lot of times because of your extreme pursuit and slightly stubborn craftsman spirit, even in the face of the same business requirements, you can achieve the best code structure and the most extensible technical architecture.
Don't let anything that doesn't give you the guidance to improve affect you to give up your pursuit!
.
Recommended reading
- 1. Relearn Java design mode: Actual factory method mode (multiple types of commodity award scenario)
- 2. Relearn Java design pattern: Actual Abstract Factory Pattern (replace Redis dual cluster upgrade scenario)
- 3. Relearn Java design mode: Actual Constructor mode (decoration material combination package selection scenario)
- 4. Relearn Java design mode: actual combat prototype mode (multiple sets of tests with each question and answer out of order)
- 5. Relearn Java design mode: actual bridge mode (multi-payment channel “wechat, Alipay” and multi-payment mode “face brush, fingerprint” scenario)