新手问题 如何优雅的重构这段代码?

huhongda · 2015年07月17日 · 最后由 lolychee 回复于 2015年07月17日 · 2516 次阅读

问题

在跨库事务操作时,我们会用上这样的代码

A::User.transaction do
   B::User.transaction do
      C::User.transaction do
           D::User.transaction do

           end
      end
   end
end

有 A,B,C,D 四个库需要同时做事务操作(真实环境确实有这么多库)

为了完成以上的代码我这样做了一个简化

module Support
  module Consistency
    def self.transaction(*args, &block)
      invalid_consts = args.select { |const| !const.respond_to?(:transaction)}

      if invalid_consts.present?
        raise "#{invalid_consts.map(&:to_s).join(',')}, they arent the sub class for ActiveRecord::Base".colorize(:red)
      end

      # TODO: I think there are better codes
      cmd = ''
      args.each do |const|
        cmd << "#{const}.transaction {"
      end
      cmd << 'yield block'
      args.each do |_|
        cmd << '}'
      end
      eval cmd
    end
  end
end
Support::Consistency.transaction(A::User, B::User, C::User, D::User).transaction do
  ....
end

请问有更优雅的办法重构这块代码么?

mock 代码:

module A
  class User

    def self.transaction(&block)
      puts "A::User transaction call #{block.call}"
    end

  end
end


module B
  class User

    def self.transaction(&block)
      puts "B::User transaction call #{block.call}"
    end

  end
end



module C
  class User

    def self.transaction(&block)
      puts "C::User transaction call #{block.call}"
    end

  end
end


module D
  class User

    def self.transaction(&block)
      puts "D::User transaction call #{block.call}"
    end

  end
end

解决代码:

def tr(*args, &block)
  bl = block
  args.reverse.each {|arg|
   # 这里不能使用 bl = -> { arg.transaction(&bl)}, 否则 lambda 的闭包作用会导致 SystemStackError
    bl = create_tr_lambda(arg, &bl)
  }
  bl.call
end

def create_tr_lambda(arg, &block)
  -> {arg.transaction(&block)}
end

tr(A::User, B::User, C::User, D::User) do
  puts "begin transaction from D::user"
end

分析过程:

inner_bl = -> {}
d = -> {D::User.transaction(&inner_bl)}
c = -> {C::User.transaction(&d)}
b = -> {B::User.transaction(&c)}
a = -> {A::User.transaction(&b)}
2 楼 已删除

@kayakjiang 果然优雅多了~

需要 登录 后方可回复, 如果你还没有账号请 注册新账号