用于必须在Ruby中输出报表的计算的设计模式

时间:2017-11-28 20:23:24

标签: ruby design-patterns

我有一个进行计算的课程。大多数情况下,它在代码中用于输出单个值:

使用:value = Calculator.new(1234).output

这是类定义的一个例子:

class Calculator

  def initialize(parameter_1)
    @parameter_1 = parameter_1
  end

  def output 
    op_1_result = operation_1(@parameter_1)
    op_2_result = operation_2(op_1_result)

    operation_3(op_2_result)
  end

  def operation_1(param)
  ...

end

但有时用户必须打印计算报告,显示计算中的许多变量。

我实现的第一个解决方案是在初始化时传递一个参数,告诉类它应该为报告保存一些内部变量,如下所示:

class Calculator

  attr_reader :report

  def initialize(parameter_1, reporting=false)
    @parameter_1 = parameter_1
    @reporting = reporting
    @report = {}
  end

  def output 
    op_1_result = operation_1(@parameter_1)
    @report[:op_1_result] = op_1_result if @reporting

    op_2_result = operation_2(op_1_result)
    @report[:op_2_result] = op_2_result if @reporting

    operation_3(op_2_result)
  end

  def operation_1(param)
  ...

end

然后,当我想得到那些中间变量时,我会:

calculator = Calculator.new(1234, true) # reporting activated
report = calculator.report

report[:op_1_result] # one of the intermediate variables of the calculation

这是否打破了单一责任原则,因为班级现在正在计算价值并同时报告?

有没有更好的方法来实现这一点,我可以在需要时快速计算输出结果的设计模式,并在没有所有ifs的情况下显示所需的报告?

对此有任何启发和意见将非常感谢!

Obs(另一个讨论):我已经读过,只是输出一个值的更实用的方法将是一件好事。但这让我想知道如何在需要时显示那些内部中间值。更多功能程序员如何做到这一点......?

3 个答案:

答案 0 :(得分:2)

我猜“构建器模式”是合适的,“报告板”应该从外部注入。

class Calculator

  def initialize(*parameters)
    @parameters = parameters
  end

  def report_to(report_pad)
    @report_pad = report_pad
    self
  end

  def output()
    ret = @parameters[0].to_i + @parameters[1].to_i 
    report('Did p0  + p1')

    ret
  end

  private

  def report(message)
    @report_pad << "\n".prepend(message) if @report_pad.respond_to? '<<'
  end

end

####

reports = ""

result = Calculator
  .new(1, 2)
  .report_to(reports)
  .output()

puts result, reports

答案 1 :(得分:1)

为什么不让所有中间结果成为公共方法并将结果链接到最终输出中?

也许是这样的:

class Calculator
  def initialize(parameter)
    @parameter = parameter
  end

  def output 
    op_3_result
  end

  def op_1_result
    @op_1_result ||= operation_1(parameter)
  end

  def op_2_result
    @op_2_result ||= operation2(op_1_result) 
  end

  def op_3_result
    @op_3_result ||= operation3(op_2_result) 
  end

  private

  def operation1(arg)
    # ...
  end

  def operation2(arg)
    # ...
  end

  def operation3(arg)
    # ...
  end

  attr_reader :parameter
end

这将允许您在同一个实例上调用所需的任何内容:

calculator = Calculator.new(1234)
calculator.output       #=> the final result
calculator.op_2_result  #=> the intermediate result of step 2

答案 2 :(得分:1)

您可以使用不同的模式,.ag-header-select-all是自己的类,并允许它在关闭报告时通过。这是一个简单的例子:

Report

然后你可以这样使用

class Calculator
  attr_reader :report
  def initialize(parameter_1, reporting=false)
    @parameter_1 = parameter_1
    @report = Report.new(reporting)
  end
  def output 
    op1 = operation_1(report.capture(:param1,@parameter_1))
    report.capture(:op1,op1)
    op2 = report.capture(:op2) { operation_2(op1) }
    operation_3(op2)
  end
  def operation_1(param);
    param + 7
  end
  def operation_2(param); 
    param - 3
  end
  def operation_3(param); 
    param * 2
  end
end

class Report
  attr_reader :reporting, :reportables
  def initialize(reporting)
    @reporting = reporting
    @reportables = {}
  end
  def capture(key, val=nil,&block)
    warn "Block supercedes value" if val && block_given?
    result = block_given? ? block.call : val  
    @reportables[key] = result if reporting
    result
  end 
  def [](key)
    return 'No Reporting' unless reporting
    @reportables[key]
  end
end

这样,每个步骤都可以使用一个块来处理应该捕获结果的更复杂的项目,或者只是在你选择的时候传递一个值,并且像c = Calculator.new(12) c.output #=> 32 c.report[:op1] #=> "No Reporting" c = Calculator.new(12, true) c.output #=> 32 c.report[:op1] #=> 19 c.report[:param1] #=> 12 这样的中间步骤(在这种情况下)不需要“捕获“只能流过。

如果报告已关闭,则所有内容都会流过,并且会忽略捕获。

你的operation_3方法看起来也像这样(根本没有中间本地人,虽然它确实会损害可读性)

#output