实例解析Ruby设计模式编程中Strategy策略模式的使用

2019-09-25 09:38:38刘景俊

好了,完工!你自信满满。
你的leader开完会后,过来看了下你的成果。总体来说,他还挺满意,但对于你使用的SQL语句拼装算法,他总是感觉有些不对劲,可是又说不上哪里不好。于是他告诉了你另一种拼装SQL语句的算法,让你加入到代码中,但是之前的那种算法也不要删除,先保留着再说,然后他又很忙似的跑开了。于是,你把他刚刚教你的算法加了进去,代码如下所示:

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 
    end 
    puts sql 
    result = @db.query(sql); 
    result.each_hash do |row| 
      #处理从数据库读出来的数据 
    end 
    #后面应将读到的数据组装成对象返回,这里略去 
  ensure 
    @db.close 
  end 
end 

可以看到,你leader教你的拼装算法,使用了一个布尔变量来控制是否需要加个or这个关键字,第一次执行for循环的时候因为该布尔值为false,所以不会加上or,在循环的最后将布尔值赋值为true,这样以后循环每次都会在头部加上一个or关键字,由于使用了头部添加or的方法,所以不用再担心SQL语句的尾部会多出一个or来。然后你为了将两个算法都保留,在find_user_info方法上加了一个参数,strategy值为1表示使用第一种算法,strategy值为2表示使用第二种算法。
这样测试代码也需要改成如下方式:

qUtil = QueryUtil.new 
qUtil.find_user_info(["Tom", "Jim", "Anna"], 2) 

这里你通过参数指明了使用第二种算法来拼装SQL语句,打印的结果和使用第一种算法是完全相同的。
你立刻把你的leader从百忙之中拖了过来,让他检验一下你当前的成果,可是他还是一如既往的挑剔。
“你这样写的话,find_user_info这个方法的逻辑就太复杂了,非常不利于阅读,也不利于将来的扩展,如果我还有第三第四种算法想加进去,这个方法还能看吗?”  你的leader指点你,遇到这种情况,就要使用策略模式来解决,策略模式的核心思想就是把算法提取出来放到一个独立的对象中。
为了指点你,他不顾自己的百忙,开始教你如何使用策略模式进行优化。
首先定义一个父类,父类中包含了一个get_sql方法,这个方法就是简单的抛出了一个异常: