It’s a little clickbait, but it’s short enough to look at
background
Recently, I have been participating in the development of the engine part of the company’s new operating system, writing lots and lots of code, and gradually came up with some simple ideas of my own for such a business solution. What operations systems do is support operations staffing activities. The so-called activity can be simply defined as: a set of conditions are met to perform some (some) action. Code for programmers: execute some function after a bunch of if else.
if else
Most programmers would probably laugh at the idea of writing if else, since most of the code on any system is if else. The business system is the if else heap for PM requirements, and the basic service is the IF else heap for OS resources and network problems. As more and more requirements proliferate, how to manage the if else pile has spawned design patterns that divide the if else into smaller granularity by various means. Of course, there is no set of methodologies that can solve the problem of if else complexity, because if else reflects the needs of the real business and represents the complexity of the business. As Lu Xun or Ma Yun or Buffett said: “No matter what method is used, if else will not disappear into thin air, only from one place to another place”. However, the readability, maintainability, and extensibility of code can be improved in specific scenarios and specific businesses.
The rule engine
Rule engines are not new and have been around for a long time, and there are various rule engines available. For example, the popular iptables is a rule engine, and crontab is a rule engine. The business logic we write is really a rules engine, but in a less obvious way. For example, “If the reset password does not contain case and punctuation, it will not be submitted”, “if the user is a VIP lottery, no ads will be played but pop-ups will be given to him”… The business logic, no matter how optimized, must have branches of code to deal with. The rules engine minimizes development by providing syntactic sugar that programming languages don’t have through a custom set of grammars (DSLS). At the same time, most rules need to support arbitrary configuration, so DSLS are mostly interpreted execution. The user configures a rule in the background. For example, in the game, the user will deduct 20 points after 1000 minutes online:
When { ? customer: Customer(totalTime >=1000); } Then { execute {? Customer. SetAmount (getAmount () 20.00); }Copy the code
The most commonly used rule engines on the market are so heavy that users have to learn their DSLS to use them, which are mostly JAVA packages. Here is an article comparing several rule engines. I can’t really comment on it without using it in depth, but with so many concepts and limited scenarios, I think it’s always a temporary solution because of the simplicity of the road. And as DSLS get more and more complex, there are few real uses for syntactic sugar, and you end up with just another crappy and domain-limited programming language. Nonetheless, rules engines are attractive to software companies that offer common solutions because of the dynamic addition of rules and the ease with which old rules can be reused. After all, the code doesn’t need to move, add some rules and it can be sold again.
But for business development RD, the requirement is actually very simple, if one thing:
- correct
- simple
- Can reduce the tedious work
Then it’s an attractive solution, and if it’s faster, it’s a brilliant idea. I think Linq is a great solution for reducing if else.
Linq
Linq is a common technique in C#, and is a delight to use. It’s a syntactic sugar, and the compiler compiles into actual code rather than interpreting execution at run time, so it’s efficient. Linq code looks something like this:
// Specify the data source.
int[] scores = new int[] { 97, 92, 81, 60 };
// Define the query expression.
IEnumerable<int> scoreQuery =
from score in scores
where score > 80
select score;
Copy the code
As you can see, some of the logic in your code would be very simple if you could express it this way. Linq basically moves SQL statements into C#, the only difference being that it puts select last and SQL first. This is also due to the IDE’s need for intelligent hints, rather than the query structure itself. By putting select last, the IDE knows which variables you have used before and can analyze what you can select to suggest. However, Linq is a C# technology that is strongly compiler dependent, and it is not easy to port to other languages. Of course, the community is also experimenting with linQ-Go, a port of Go. However, as you know, since GO itself doesn’t support generics, and the compiler doesn’t support LINQ syntax, it’s certainly still crappy to use, and interface{} and type inference are bound to fly…
sql
In fact, the where part of our common SQL is complete, and Linq has moved SQL into C#. In other words, the WHERE part of SQL can combine arbitrary Boolean logic. All RDS run SQL, and often run SQL, and since it’s too much trouble to do things from the compiler’s side (especially since GO is a notoriously simple language, and the authorities abhors the use of keywords, let alone syntactic sugar), kill mysql queries. Explain the part of the function that performs the WHERE comparison as a function, for example to check whether a person is a top programmer:
sql := `sex='male' and ( dislike in ('girl', 'woman', 'female') or hobby in ('dress up', 'makeup') )`
Copy the code
However, since there is no table data, the actual comparison data needs to be provided by the business side. The above functions can be written as:
func isTopProgrammer(userInfo User) bool {
sql := `sex='male' and ( dislike in ('girl', 'woman', 'female') or hobby in ('dress up', 'makeup') )`
ok,_ := yql.Match(sql, map[string]interface{} {"sex": userInfo.Sex,
"dislike": userInfo.Dislike,
"hobby": userInfo.Hobbies,
})
return ok
}
Copy the code
Yql is actually a lib that I recently created to help you perform SQL comparisons.
In this way, the frequently changing parts of the business logic can be abstracted into configuration files, such as:
// Define the different case handlers
type handleFunc func(map[string]interface{}) error
var handlers = map[int]handleFunc {
1: sendEmail,
2: sendCoupon,
3: sendSms,
4: sendAlert2Boss,
5: runAway,
}
Parse the data from the current request
data := resolveDataFromPostParams(request.Body)
// Load rules from configuration files
rules := loadRuleFromConf()
// Enumerate each rule to match, and execute the corresponding handler if the match is successful
for _,rule := range rules {
success,err := yql.Match(rule.SQL, data)
if nil! = err || ! success {continue
}
handler := handlers[rule.ID]
handler(data)
break
}
Copy the code
Of course, there are pros and cons to this approach, such as separating rules into configuration files and having to jump from one file to another to see the code while debugging. One advantage is that you can use push platforms to update profiles in real time, so that code can be hot updated.
The last
In fact, yQL still has a lot of application scenarios (I feel), of course, there are some shortcomings, such as the function is still relatively simple, explain the execution, the speed of the fact has not been compared with who…… But I hope you can try, if it can improve work efficiency, or good. For attention, for star