简体   繁体   中英

How to associate instances from different classes in Ruby

I want to be able to initialize a new Car object and pass it a Person object in the parameters, so it can be saved in that Person's @cars array. Currently, I take this approach:

person = Person.new("Michael")
car = Car.new("Honda", "Accord")
person.add_car(car)
person.add_car(Car.new("Ford", "Taurus"))
person.add_car(Car.new("Toyota", "Prius"))
person.display

However, I'd like to be able to create a new car instance and pass it the Person object I want it associated with. For example:

person = Person.new("Michael")
Car.new("Honda", "Accord", person)
Car.new("Toyota", "Camry", person)
Car.new("Chevy", "Tahoe", person)
person.display

Is that even possible?

class Person

  attr_accessor :name

  def initialize(name)
    super
    @name = name
    @cars = []
  end

  def display
    puts "#{@name} has #{@cars.length} cars"
    puts "----------------------------"
    @cars.each do |car|
      puts "#{car.make} #{car.model}"
    end
  end

  def add_car(car)
    @cars.push(car)
  end

end 

class Car

  attr_accessor :make, :model

  def initialize(make, model)
    @model = model
    @make = make
  end

  def display
    puts "#{@make} #{@model}"
  end

end

Yes, that is possible, Car#initialize can call methods on its arguments:

class Car
  def initialize(make, model, person = nil)
    @model = model
    @make  = make
    person.add_car(self) if(person)
  end
  #...
end

This would be my implementation:

class Car 
  attr_accessor :make, :model

  def initialize(make, model)
    self.make = make
    self.model= model
  end
end

Person class

class Person
  attr_accessor :name, :cars

  def initialize(name, cars=[])
    self.name = name
    self.cars = cars || []
  end

  def add_car(*args)
    raise ArgumentError, 'invalid arguments'  if (
        (args.size > 2 or args.size == 0) or 
        (args.size == 1 and !args[0].is_a?(Car))
      )
    new_car = (args.size == 2) ? Car.new(*args) : args[0]
    self.cars << new_car
    new_car
  end

end

Now you can:

person = Person.new("Michael")
car = Car.new("Honda", "Accord")
person.add_car(car)
person.add_car("Ford", "Taurus")
person.add_car("Toyota", "Prius")
person.display

The add_car method creates a new car when make and model are passed as parameters.

Yes, it is possible as mu is too short 's answer demonstrated, but that doesn't really make sense in my opinion. Your cars can't be used in any context without a Person , and said parameter contributes no data necessary to construct the Car object.

I would design such an API as follows:

class Person
  def initialize(name)
    @name = name
  end

  def cars
    @cars ||= Array.new  # Equivalent to @cars || @cars = []
  end
end

person = Person.new 'Michael'

taurus = Car.new 'Ford', 'Taurus'
prius = Car.new 'Toyota', 'Prius'

person.cars << taurus << prius << Car.new('Honda', 'Accord')

This is a simpler and more direct form of KandadaBoggu 's implementation that takes advantage of the Array#<< method in order to naturally associate Car s with a Person , and also doubles as an attribute reader.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM