Ruby:如何使用另一个对象的getter来初始化另一个对象?

时间:2016-09-08 19:21:34

标签: ruby class object scope initialization

class Car
  attr_reader :doors, :color

  def initialize(doors, color)
    @doors = doors
    @color = color
  end
end

class Motorcycle
  attr_reader :cc, :color

  def initialize(cc)
    @cc = cc
    @color = obtain_color
  end

  def obtain_color
    'orange' if my_car.color == 'green'
    'blue'
  end
end

class Vehicles
  attr_reader :my_car, :my_motorcycle

  def initialize
    @my_car = Car.new(4, 'green')
    @my_motorcycle = Motorcycle.new(950)
  end

  def display_colors
    puts "my_car color: #{my_car.color}"
    puts "my_motorcycle color: #{my_motorcycle.color}"
  end
end

my_vehicles = Vehicles.new
my_vehicles.display_colors

毫不奇怪地产生以下错误:

in 'obtain_color': undefined local variable or method 'my_car' for #<Motorcycle:0x007fc1820690a0 @cc=950> (NameError)

为了解决这个问题,我传递了my_car这样的对象:

class Car
  attr_reader :doors, :color

  def initialize(doors, color)
    @doors = doors
    @color = color
  end
end

class Motorcycle
  attr_reader :cc, :color

  def initialize(cc, my_car)
    @cc = cc
    @color = obtain_color(my_car)
  end

  def obtain_color(my_car)
    'orange' if my_car.color == 'green'
    'blue'
  end
end

class Vehicles
  attr_reader :my_car, :my_motorcycle

  def initialize
    @my_car = Car.new(4, 'green')
    @my_motorcycle = Motorcycle.new(950, my_car)
  end

  def display_colors
    puts "my_car color: #{my_car.color}"
    puts "my_motorcycle color: #{my_motorcycle.color}"
  end
end

my_vehicles = Vehicles.new
my_vehicles.display_colors

问题:有没有办法在my_car.color的初始化内调用Motorcycle而无需传入my_car对象?

2 个答案:

答案 0 :(得分:2)

简短的回答是否定的,因为摩托车如何知道要访问哪个Car实例?

这引出了我们更长的答案,即:为什么摩托车应该关注汽车的颜色?答案当然是,它不应该。

为了让您更容易用具体的术语思考,让我们将Vehicles类重命名为Garage。现在,如果我们有两个Garage实例,那该怎么办:my_garageyour_garage。大概是相同的颜色规则&#34;将适用于每个实例,但一个实例不会对另一个实例产生任何影响。如果你的车库里有一辆绿色的汽车,我的矿车里面没有橙色摩托车 - 除非我在自己的车库里有一辆绿色的汽车。

由于摩托车不应该关注汽车的颜色(或者即使汽车存在),我们如何使摩托车的颜色依赖于汽车的颜色呢?我们把它留给车库:

class Motorcycle
  attr_reader :cc, :color

  def initialize(cc, color)
    @cc = cc
    @color = color
  end
end

class Garage
  attr_reader :my_car, :my_motorcycle

  def initialize
    @my_car = make_car
    @my_motorcycle = make_motorcycle
  end

  def make_car
    Car.new(4, "green")
  end

  def make_motorcycle
    if @my_car && @my_car.color == "green"
      color = "orange"
    else
      color = "blue"
    end

    Motorcycle.new(950, color)
  end

  def display_colors
    puts "my_car color: #{my_car.color}"
    puts "my_motorcycle color: #{my_motorcycle.color}"
  end
end

这有点人为,因为Car的颜色在make_car中是硬编码的,所以我们也可以硬编码摩托车的颜色,但你明白了。

当对象相互关联时,试图找出逻辑放置的位置,事实证明,真的很难。你会变得更好,但是如果你想为自己节省很多头脑,我高度建议阅读Sandi Metz' Practical Object-Oriented Design in Ruby。它不仅是有史以来最好的Ruby书籍之一,也是有史以来最好的OOP书籍之一。

P.S。你的代码中有一个错误,在这里:

def obtain_color(my_car)
  'orange' if my_car.color == 'green'
  'blue'
end

此方法将始终返回"blue"。你的意思可能是:

def obtain_color(my_car)
  return 'orange' if my_car.color == 'green'
  'blue'
end

答案 1 :(得分:1)

为什么不从Vehicle类中设置摩托车的颜色。

您只需要从其他类(通过使用attr_accessor)访问颜色实例变量,然后添加一个方法来设置Vehicles类中的颜色。

class Car
  attr_reader :doors, :color

  def initialize(doors, color)
    @doors = doors
    @color = color
  end
end

class Motorcycle
  attr_reader :cc
  attr_accessor :color

  def initialize(cc)
    @cc = cc
    @color = nil
  end
end

class Vehicles
  attr_reader :my_car, :my_motorcycle

  def initialize
    @my_car = Car.new(4, 'green')
    @my_motorcycle = Motorcycle.new(950)
    set_motocycle_color
  end

  def set_motocycle_color
    my_motorcycle.color = (my_car.color == 'green') ? 'blue': 'orange'
  end

  def display_colors
    puts "my_car color: #{my_car.color}"
    puts "my_motorcycle color: #{my_motorcycle.color}"
  end
end

my_vehicles = Vehicles.new
my_vehicles.display_colors

# my_car color: green
# my_motorcycle color: blue