0
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

Not surprisingly produces the following error:

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

To fix this, I passed in the my_car object like this:

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

Question: Is there a way to call my_car.color from within the initialization of Motorcycle without having to pass in the my_car object?

2
  • Nope, you have to pass in data that isn't a member of the class. Commented Sep 8, 2016 at 19:49
  • Is this possible using Forwardable ? Commented Sep 8, 2016 at 20:00

2 Answers 2

2

The short answer is no, because how would the Motorcycle know which instance of Car to access?

Which leads us to the longer answer, which is: Why should the Motorcycle care what color the Car is? The answer, of course, is that it shouldn't.

Just to make this easier to think about in concrete terms, let's rename the Vehicles class to Garage. Now, what if we had two instances of Garage: my_garage and your_garage. Presumably the same "color rules" would apply to each instance, but one instance wouldn't have any effect on the other. If you had a green Car in your Garage, I wouldn't have an orange Motorcycle in mine—unless I had a green Car in my own Garage.

Since the Motorcycle shouldn't care what color the Car is (or even if a Car exists), how do we make the color of the Motorcycle depend on the color of the Car anyway? We leave it up to the Garage:

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

This is a little contrived since the Car's color is hard-coded in make_car, so we could just hard-code the Motorcycle's color, too, but you get the idea.

Trying to figure out where to put logic when objects interrelate like this is, as it turns out, really hard. You'll get better at it, but if you want to save yourself a lot of head-scratching I highly recommend reading Sandi Metz' Practical Object-Oriented Design in Ruby. It's not just one of the best Ruby books ever written, it's one of the best OOP books ever written, too.

P.S. You had a bug in your code, here:

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

This method will always return "blue". You probably meant this instead:

def obtain_color(my_car)
  return 'orange' if my_car.color == 'green'
  'blue'
end
Sign up to request clarification or add additional context in comments.

1 Comment

The deeper meaning of Motorcycle shouldn't care what color the Car is (or even if a Car exists) seems to be a key concept of OOP. I'll check out the book!
1

Why not set the motorcycle's color from the Vehicles class.

You simply need to make the color instance variable accessible from other class (by using attr_accessor) and then add a method to set the colour from within the Vehicles Class.

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.