Ruby—从另一个方法中提取较小的方法(将字符串转换为两个数字和一个符号)

Ruby—从另一个方法中提取较小的方法(将字符串转换为两个数字和一个符号),ruby,string,methods,private,single-responsibility-principle,Ruby,String,Methods,Private,Single Responsibility Principle,我有一个方法(land_connected_rover(坐标)),它接受一个参数(包含两个数字和一个字母的字符串,空格分隔)并基于此执行提示,我的问题是我很难从该方法提取我的分配,我尝试了私有方法,但一旦我这样做,我注入的类中没有一个可以再访问这些变量了。我想要一个优雅的、基于SRP的解决方案,而不是当前混乱的解决方案。。。对不起,伤了你的眼睛 “x”、“y”和“position”变量名对于其他类很重要,因为它们依赖于这些提示 我想提取将x、y和位置变量名分配给字符串右侧部分的步骤 class

我有一个方法(land_connected_rover(坐标)),它接受一个参数(包含两个数字和一个字母的字符串,空格分隔)并基于此执行提示,我的问题是我很难从该方法提取我的分配,我尝试了私有方法,但一旦我这样做,我注入的类中没有一个可以再访问这些变量了。我想要一个优雅的、基于SRP的解决方案,而不是当前混乱的解决方案。。。对不起,伤了你的眼睛

“x”、“y”和“position”变量名对于其他类很重要,因为它们依赖于这些提示

我想提取将x、y和位置变量名分配给字符串右侧部分的步骤

class Controller

  attr_reader :current_rover, :current_surface

  def initialize
    @current_surface = nil
    @current_rover ||= []
  end

  def connect_to_surface(destination)
    @current_surface = destination
  end

  def connect_to_rover(rover)
    @current_rover = rover
  end

  def land_connected_rover(coordinates)
    coordinates = coordinates.delete(' ')
    x = coordinates[0].to_i
    y = coordinates[1].to_i
    position = coordinates[2].to_sym
    self.current_rover.x_coordinates = x
    self.current_rover.y_coordinates = y
    self.current_rover.position = position
    add_to_grid(x,y)
  end

  def navigate(command)
    self.current_rover.read_input(command)
  end

  private

  def add_to_grid(x,y)
    @current_surface.record_on_map(x,y)
  end

  def turn_on_rover
    @current_rover.online = true
  end
end
我真的很感谢你的帮助!对不起,如果我问错了所有的问题,我是新来的。

关于。有可能过度考虑。担心这种方法可能不值得你花时间。这是一个由简单语句组成的八行方法,很容易理解,也很有效。可能已经测试过了,但如果没有,那最好花点时间

例如,您将
@current_rover
初始化为一个列表,但它被用作用户对象。我怀疑如果用户不设置
@current\u rover
,很多事情都会变得混乱。那将是一件值得花费时间的事情。您可以进行一些测试,尝试使用一个新初始化的对象,并确保它们产生有意义的异常,或者您可以绕过所有这些复杂的步骤,决定它必须用一个漫游者进行初始化

让我们把重构当作一个练习


首先,让我们试着通过对每一块事物进行注释来解释它们所做的事情,来描述
land_connected_rover
所做的所有事情

  def land_connected_rover(coordinates)
    # Parse coordinates.
    coordinates = coordinates.delete(' ')
    x = coordinates[0].to_i
    y = coordinates[1].to_i
    position = coordinates[2].to_sym

    # Set rover coordinates.
    self.current_rover.x_coordinates = x
    self.current_rover.y_coordinates = y
    self.current_rover.position = position

    # Add something to the grid for some reason.
    add_to_grid(x,y)
  end
像这样的注释对于提取方法来说非常有用。我们需要一些东西来解析坐标。设置坐标的东西。还有一些东西可以给网格添加坐标。我们已经有了最后一个,所以提取另外两个

    def parse_coordinates(input)
       input = input.delete(' ')

       return {
           x:   coordinates[0].to_i,
           y:   coordinates[1].to_i,
           position: coordinates[2].to_sym
       };
    end

    def set_rover_coordinates(coordinates)
        @current_rover.x_coordinates = coordinates[:x]
        @current_rover.y_coordinates = coordinates[:y]
        @current_rover.position = coordinates[:position]
    end
现在,这些可以被记录、重用和单元测试。测试可能表明,当坐标未解析或未设置
@current_rover
时,需要添加错误处理

我使用
@current_rover
与使用实例变量进行内部访问而不是访问器的其余代码保持一致。无论哪种方式都有争议,选择一种

然后把它们重新组合起来

  def land_connected_rover(input)
    set_rover_coordinates( parse_coordinates(input) )
    add_to_grid(x,y)
  end

从那里我们可以进行更多的观察。为什么控制器编写方便的方法来设置漫游者的属性<代码>设置探测车坐标可能会移动到探测车

  def land_connected_rover(input)
    @current_rover.set_coordinates( parse_coordinates(input) )
    add_to_grid(x,y)
  end
谁应该解析坐标?我认为路虎和控制器都需要这样做。这表明您需要一个坐标类

  # In Controller
  def land_connected_rover(input)
    @current_rover.set_coordinates( Coordinate.from_a(input) )
    add_to_grid(x,y)
  end

  # In Rover
  def set_coordinates(coordinates)
    @x_coordinates = coordinates.x
    @y_coordinates = coordinates.y
    @position = coordinates.position
  end
现在我们观察到,与其让漫游者的坐标是一组属性,不如让它有一个单独的坐标属性来获取坐标对象

  # In Rover
  attr :coordinates

  # In Controller
  def land_connected_rover(input)
    @current_rover.coordinates( Coordinate.from_a(input) )
    add_to_grid(x,y)
  end
现在我们有了坐标对象,控制器可以被传递一个坐标对象,而不用担心规范化它的输入

  def land_connected_rover(coordinates)
    @current_rover.coordinates( coordinates )
    add_to_grid(x,y)
  end
让调用方处理规范化

  controller.land_connected_rover( Coordinate.from_a([10,20,30] ) )
现在您有了坐标对象,调用者就可以使用它们了,大多数转换的需要可能都会消失


从这里开始,我的下一个关注点是漫游者和地面之间的坐标复制。月球车有自己的坐标,表面似乎独立跟踪月球车的坐标。我想两者必须保持同步?如果是这样的话,这就增加了复杂性和漏洞的风险。或者表面只是跟踪探测器最初着陆的地方?这是值得考虑的问题。
请注意,在我开始重构之前,大多数情况都不明显。最初,我停止了对land_connected_rover的第一次重构,但后来又进一步思考,提出了坐标类,然后它从那里级联而来。我认为最终的结果要好得多,远远超出了我一开始的预期


所以。。。是的,有时确实担心八行法

我最终得到了这个解决方案:

class Controller

  attr_reader :current_rover, :current_surface

  def initialize
    @current_surface ||= nil
    @current_rover ||= nil
  end

  def connect_to_surface(destination)
    @current_surface = destination
  end

  def connect_to_rover(rover)
    @current_rover = rover
  end

  def is_mission_ready?
    @current_surface != nil && @current_rover != nil
  end

  def land_connected_rover(coordinates)
    raise 'Mission is not ready to launch yet!' if !is_mission_ready?
    x, y, position = parse_coordinates(coordinates)
    current_rover.x_coordinates = x
    current_rover.y_coordinates = y
    current_rover.position = position
    add_to_grid
  end

  def navigate(command)
    raise 'Navigation is not ready to start yet!' if !is_mission_ready?
    current_rover.read_input(command)
  end

  private

  def parse_coordinates(coordinates)
    fields = coordinates.split(" ")
    [fields[0].to_i, fields[1].to_i, fields[2].to_sym]
  end

  def add_to_grid
    x = current_rover.x_coordinates
    y = current_rover.y_coordinates
    position = current_rover.position
    @current_surface.record_on_map(x,y)
    puts "Rover landed, currently facing: #{current_rover.position}, x-coordinates: #{x}, y-coordinates: #{y}"
    @current_surface.grid
  end
end

对于着陆的漫游者来说,这并不是那么糟糕:)。也许你可以移动到(x,y),设置x,y和位置。非常感谢你在回答问题上花的时间和帮助,非常感谢。最后我做了一个简短的演讲,但我的面试很成功,所以,再次感谢!)