Today your leader to find you, hope you can help him a little busy, he is now anxious to go to the meeting. What can I do for you? You're curious.
He said to you, the current database of your project has a user information table, which contains a very user data, now need to complete a selective query user information function. He said he'd pass it on to you. An array that contains a lot of user names, and you need to check out their corresponding data according to the username.
This function is very simple, you readily agreed. Since your project is using a MySQL database, you quickly write the following code:
Require ' MySQL '
class queryutil
def find_user_info usernames
@db = mysql.real_connect ("localhost", "root" , "123456", "Test", 3306);
sql = ' SELECT * from User_info where '
usernames.each do |user|
SQL << "username =" "
SQL << user
sql <<" ' or "
end
puts sql result
= @db. Query (S QL);
Result.each_hash do |row|
#Processing data read from the database
End
#The data should be assembled into an object and returned later
, omit the
ensure
@db.
Here, the SQL statements are assembled according to the incoming user array group, and then the corresponding rows are found in the database. For the sake of debugging, you will also print out the assembled SQL statements.
Then, you write the following code to test this method:
Qutil = queryutil.new
qutil.find_user_info ["Tom", "Jim", "Anna"]
Now run the test code and you find that the program has gone wrong. So you immediately checked the printed SQL statement, and found the problem.
SELECT * from user_info where username = ' Tom ' or username = ' Jim ' or username = ' Anna ' or
The assembled SQL statements add an OR keyword at the end! Because the For loop does not have to add or to the last piece of data, the code is stupid enough to add an or keyword to the last piece of data, causing the SQL statement syntax to go wrong.
What can we do about it?
There it is! You have a flash of inspiration and come up with a solution. After the SQL statements have been assembled, it is better to intercept the last or previous position. So you change the code to look like the following:
Require ' MySQL '
class queryutil
def find_user_info usernames
@db = mysql.real_connect ("localhost", "root" , "123456", "Test", 3306);
sql = ' SELECT * from User_info where '
usernames.each do |user|
SQL << "username =" "
SQL << user
sql <<" ' or "
end
sql = sql[0. -"or". Length]
puts SQL result
= @db. query (SQL);
Result.each_hash do |row|
#Processing data read from the database
End
#The data should be assembled into an object and returned later
, omit the
ensure
@db.
Using the Intercept substring method of string, only to the part before the last or, running the test code again, everything is fine, and the printed SQL statement looks like this:
SELECT * from user_info where username = ' Tom ' or username = ' Jim ' or username = ' Anna '
All right, finish it! You are full of confidence.
After the meeting of your leader, come and see what you have done. Overall, he was quite satisfied, but for the SQL statement you used to assemble the algorithm, he always felt something was wrong, but he couldn't say where it was bad. So he tells you another algorithm for assembling SQL statements, which allows you to add to the code, but don't delete the previous algorithm, keep it, and then run away. So, you add the algorithm that he just taught you, and the code looks like this:
require ' MySQL ' Class Queryutil def find_user_info (usernames, strategy) @db =
Mysql.real_connect ("localhost", "root", "123456", "Test", 3306);
sql = ' SELECT * from User_info where ' if strategy = 1 Usernames.each do |user| SQL << "username =" "SQL << User sql <<" ' or "end sql = sql[0.
-"or". Length] elsif strategy = = 2 Need_or = False Usernames.each do |user|
SQL << "or" If Need_or SQL << "username = '" SQL << User sql << ""
Need_or = True End-puts SQL result = @db. query (SQL);
Result.each_hash do |row| #Process the data read from the database end # The data read should be assembled into an object and returned, omit the ensure @db. Close End
You can see that you leader to teach you the assembly algorithm, using a Boolean variable to control whether you need to add an or this keyword, the first time the for loop execution because the Boolean value is False, so will not add or, at the end of the loop to assign the Boolean value to True, After that, the loop will add an or keyword to the head each time, and you don't have to worry about adding an or to the end of the SQL statement because you used the method of using the head to add or. And then you save two algorithms, add a parameter to the Find_user_info method, and a strategy value of 1 means the first algorithm, and the strategy value of 2 to use the second algorithm.
This test code also needs to be changed to the following ways:
Qutil = queryutil.new
qutil.find_user_info (["Tom", "Jim", "Anna"], 2)
Here you indicate by parameter that the second algorithm is used to assemble the SQL statement, and that the printed result is exactly the same as using the first algorithm.
You immediately dragged your leader from your busy schedule, let him test your current results, but he is still as picky as ever.
"As you write this, the logic of the find_user_info is too complicated to read, and not conducive to future expansion, if I have a third fourth algorithm to add, this method can still see?" "Your leader to guide you, in this case, we need to use the strategy model to solve, the core idea of the strategy model is to extract the algorithm into a separate object."
In order to guide you, he ignored his busy schedule, began to teach you how to use the Strategy model for optimization.
First you define a parent class, and the parent class contains a Get_sql method that simply throws an exception:
Class Strategy
def get_sql usernames
raise "you should override this method in subclass."
End End
Then the definition of two subclasses inherits the above parent class and adds the two algorithms for assembling SQL statements to two subclasses respectively:
Class Strategy1
def get_sql usernames
sql = "SELECT * from User_info where"
Usernames.each do |user|
SQL << "username =" "
SQL << user
sql <<" ' or "
end
sql = sql[0. -' or '. Length]
end
class Strategy2
def get_sql usernames
sql = ' select * from User_info wher E "
need_or = False
Usernames.each do |user|
SQL << "or" If Need_or
sql << "username = '"
SQL << user
sql << "'"
need_or = True End End
You can then call the strategy Get_sql method in the Queryutil Find_user_info method to get the assembled SQL statement, as shown in the following code:
Require ' MySQL '
class queryutil
def find_user_info (usernames, strategy)
@db = Mysql.real_connect (" LocalHost "," root "," 123456 "," Test ", 3306);
sql = Strategy.get_sql (usernames)
puts SQL result
= @db. query (SQL);
Result.each_hash do |row|
#Processing data read from the database
End
#The data should be assembled into an object and returned later
, omit the
ensure
@db.
Finally, when the test code calls the Find_user_info method, it only needs to indicate which policy object you want to use:
Qutil = queryutil.new
qutil.find_user_info (["Tom", "Jim", "Anna"], strategy1.new)
qutil.find_user_info ([" Jac "," Joe "," Rose "], strategy2.new
The printed SQL statement is not expected at all, as follows:
SELECT * from user_info where username = ' Tom ' or username = ' Jim ' or username = ' Anna '
Select # from User_info where Username = ' Jac ' or username = ' Joe ' or username = ' Rose '
After using the policy mode modification, the code readability and extensibility have greatly improved, even if you need to add a new algorithm, you are extremely easy!
Example of a combination of a policy model and a simple factory model
Demand:
Shopping center cashier software, according to the customer purchase items of unit price and quantity, calculate the cost, there will be promotional activities, 80 percent dozen, 300 minus 100 and so on.
1. Use Factory mode
#-*-Encoding:utf-8-*-#Cash charge abstract class class Cashsuper def accept_cash (money) End #Normal charge subclass class Cashnormal <Cas
Hsuper def accept_cash (money), End #Discounted charge subclass class Cashrebate <Cashsuper attr_accessor: mony_rebate
def initialize (mony_rebate) @mony_rebate = Mony_rebate End def accept_cash (money) money * mony_rebate End #rebate charge subclass class Cashreturn <Cashsuper attr_accessor: mony_condition: Mony_return def initialize (mony_co
Ndition, Mony_return) @mony_condition = mony_condition @mony_return = Mony_return End def accept_cash (money) If Money> Mony_condition- (money / mony_condition) * Mony_return end end #cash fee factory class class Cash Factory def self.create_cash_accept (type) case type when 'normal charge' cashnormal.new () when 'call 80 percent' cash Rebate.new (0.8) When 'full 300 reduction' cashreturn.new (300,100) end end cash0 = cashfactory.create_cash_accept ('normal charge') p Cash0. accept_Cash (m) cash1 = Cashfactory.create_cash_accept ('Play 80 percent') p Cash1.accept_cash (m) cash2 = Cashfactory.create_cash_ Accept (' full 300 reduction ') p Cash2.accept_cash (700)
The custom discount ratio and the number of full cuts are achieved.
The problems that exist:
When you increase the type of activity, call 50 percent, 500 minus 200, and you need to add a branching structure to the factory class.
The activity is various, also may increase the integral activity, full 100 plus 10 integral, the integral may collect the activity prize, then must add one subclass.
But every time you increase the activity, you have to modify the factory class, is a bad way to deal with the algorithm changes, there should be a better way.
2. Strategy model
The Cashsuper and subclasses are invariant, adding the following:
Class Cashcontext
Attr_accessor:cs
def initialize (c_super)
@cs = C_super
end
def result (money)
Cs.accept_cash End
type = ' Call 80 percent '
cs=case type when
' normal charge '
Cashcontext.new (Cashnormal.new ()) when
' call 80 percent '
cashcontext.new (cashrebate.new (0.8)) when
' full 300 minus 100 '
cashcontext.new (Cashreturn.new (300,100))
end
P Cs.result (700)
The Cashcontext class encapsulates different cashsuper subclasses and returns the corresponding result. That is, the different algorithms are encapsulated, no matter how the algorithm changes. Results can be obtained using result.
However, there is a problem, users need to make a decision to choose which algorithm to use. Can be combined with a simple workshop class.
3. Strategy and simple workshop combination
Class Cashcontext
Attr_accessor:cs
def initialize (type) case
type when
' normal charge '
@cs = Cashnormal.new () When
' play 80 percent '
@cs = cashrebate.new (0.8) When
' full 300 reduction '
@cs = cashreturn.new (300,100) End-def result (money)
Cs.accept_cash end
cs=cashcontext.new ( ' hit 80 percent ')
P Cs.result (700)
Different subclasses are instantiated in the Cashcontext. (Simple Factory)
The process of selecting subclasses is moved internally to encapsulate the algorithm (policy pattern).
The caller can get the final result by using a simpler, incoming parameter (activity type, original price).
Users here need only know a class (Cashcontext), and a simple workshop needs to know two classes (Cashfactory Accept_cash methods and cashfactory), which means the encapsulation is more thorough.