Краща практика з'єднання об'єктів між ними в рубіні

Я не можу знайти хороший спосіб реалізації рейок поведінки навігаційних рейок об'єктів у чистому рубіновому скрипті.

Скажімо, у мене є об'єкт класу Parent, який має доступ до масиву об'єктів класу Child, я б хотів, щоб я маніпулював об'єктом Child, щоб легко отримати батьків, як це:

class Parent
  attr_accessor :children

  def initialize
    @children = Array.new
  end
end

class Child
end

parent = Parent.new
first_child = Child.new
second_child = Child.new
parent.children << [ first_child, second_child ]

a_child = parent.children.first

get_parent_from_child = a.child.parent

Частина, яку я зацікавлений, - це, звичайно ж, остання рядок, в якій я намагаюся отримати "батьківський" об'єкт від одного з його дітей.

Як я можу це здійснити легко і чисто?

Я думав про додавання доступу до дочірнього об'єкта, але я не знаю, як переконатися, що це значення встановлюється щоразу, коли я додаю дочірний об'єкт до батьківського об'єкта.

Чи існує легкий і чистий спосіб зробити це в рубіні?

Заздалегідь спасибі.

2

3 Відповіді

Вам не доведеться повністю розкривати ваш children , який ви знаєте, немає нічого поганого, повністю контролюючи доступ до ваших даних.

class Parent
  def initialize
    @children = [ ]
  end

  # You get a shallow copy of the children, you can
  # change the individual children but not the tree
  # structure.
  def children
    @children.dup
  end

  # We take ownership of the children when you add them.
  def add_children(*kids)
    kids.each { |k| k.parent = self }
    @children += kids
  end

  def remove_children(&block)
    @children.delete_if(&block)
  end
end

# This would probably have some sort of payload and
# a sensible "==" implementation.
class Child
  attr_reader :parent
  def parent=(p)
    raise StandardError => 'Not allowed to change parents!' if @parent
    @parent = p
  end
end

Тоді child.parent відмінно працює, і якщо ви хочете видалити дітей, ви скажете батькові, які з них видалити:

# Remove the children with even payloads.
parent.remove_children { |c| c.payload % 2 == 0 }

# Remove the children with odd payloads and copy them.
odds = [ ]
parent.remove_children do |c|
  kill_it = c.payload % 2 != 0
  odds << Child.new(c.payload) if(kill_it)
  kill_it
end
2
додано
@Барамін: Не хвилюйтеся, маючи декілька варіантів, це добре, як це відбувається з тим, що працює для вас.
додано Автор mu is too short, джерело
Гарне оновлення до відповіді крикуса, але я віддаю перевагу Яну, тому що це те, що я шукав. Дуже дякую за вашу допомогу, му (занадто короткий), хе-хе, мені подобається ваше ім'я.
додано Автор Baramin, джерело

Напишіть власний доступ для дітей у класі Parent, в якому ви встановлюєте батьківський атрибут для кожної дитини, доданої в масив. Зробіть зворотне при видаленні дітей з масиву. Ось як я це зробив би.

1
додано
Ви також можете це зробити. Виведіть клас з масиву та реалізуйте метод new_child (), який встановлює властивість parent , після чого викликає self << new_child .
додано Автор ckruse, джерело
В порядку. Я міг це зробити, додаючи add_child і delete_child до батьківського об'єкта. Я почну реалізувати це таким чином зараз, і якщо ніхто інший не має кращого рішення, він перевірить вашу відповідь. Я впевнений, що буде віддавати перевагу такий спосіб, який дозволить наприклад, наприклад, new_child = parent.children.new ().
додано Автор Baramin, джерело

Ви можете досягти поведінки has_many від Rails, використовуючи спеціальний клас контейнера, як це робить Rails.

class Parent
  attr_reader :children

  def initialize
    @children = ChildCollection.new(self)
  end
end

class Child
  attr_accessor :parent
end

class ChildCollection
  def initialize parent
    @children = []
    @parent = parent
  end

  def new
    child = Child.new
    child.parent = @parent
    @children << child
    child
  end

  def << child
    child.parent = @parent
    @children << child
    self
  end
end

І, наприклад, код для додавання дітей до батьків:

parent = Parent.new
child1 = parent.children.new
child2 = Child.new
child3 = Child.new
parent.children << child2 << child3

puts "Parent is: #{parent.object_id}"
puts "Child1's parent is: #{child1.parent.object_id}"
puts "Child2's parent is: #{child2.parent.object_id}"
puts "Child3's parent is: #{child3.parent.object_id}"

Можливо, ви захочете вибрати інші корисні методи, такі як remove і every (щоб ChildCollection містили Enumerable модуль і отримати всі цікаві речі, які поставляються разом з цим), але цього достатньо, щоб почати.

1
додано
У класі Parent я використовував attr_reader: children замість attr_accessor , оскільки ми повинні переконатися, що наш контейнер містить посилання на відповідний батьківський об'єкт . Ми все ще маємо право додавати та видаляти дітей із колекції.
додано Автор Ian Eccles, джерело
О так, це схоже на те, що я шукав. Я спробую реалізувати його в понеділок у моєму проекті. Спасибі, Ян.
додано Автор Baramin, джерело