Ruby - подклассы, использующие переменные экземпляра родительского - PullRequest
1 голос
/ 23 сентября 2019
class A
  def initialize(var)
    @var = var
  end
end

class B < A
  def initialize
  end

  def test
    puts @var
  end
end

a = A.new('hello')

b = B.new
b.test

Этот код не работает, потому что b наследуется от A, но не a.

В Ruby (или Rails), как мне получить такое поведение?Наследование полезно для обмена методами, может ли оно также делиться данными?


ОБНОВЛЕНИЕ

Этот вопрос немного сбивает с толку, поэтому я попытаюсь уточнить.

Iхотите, чтобы экземпляры B наследовали не только от класса A, но и от экземпляра A.Таким образом, я мог бы сделать что-то вроде этого:

a1 = A.new('one')
a2 = A.new('two')
b1 = a1.B.new # not ruby syntax, but this is why I'm asking the question
b2 = a2.B.new
b3 = a2.B.new

b3.test
#=> 'two'
b2.test
#=> 'two'
b1.test
#=> 'one'

Ответы [ 5 ]

1 голос
/ 23 сентября 2019

Одним из быстрых способов достижения вашей цели было бы использование малоизвестного SimpleDelegator класса:

class A
  attr_reader :var

  def initialize(var)
    @var = var
  end
end

require 'delegate'

class B < SimpleDelegator
  def test
    puts var
  end
end

a1 = A.new('one')
a2 = A.new('two')
b1 = B.new(a1)
b1 = B.new(a1)
b2 = B.new(a2)
b3 = B.new(a2)

b3.test
#=> 'two'
b2.test
#=> 'two'
b1.test
#=> 'one'

Эта статья блога («5 способов пересылки»ваша работа с каким-либо другим объектом в Ruby ") может вас заинтересовать.

1 голос
/ 23 сентября 2019

Тогда вы можете рассмотреть переменную класса:

class A
  def initialize(var)
    @@var = var
  end
end

class B < A
  def initialize
  end

  def test
    puts @@var
  end
end

a = A.new('hello')

b = B.new
b.test
#=> 'hello'
1 голос
/ 23 сентября 2019

Невозможно использовать «наследование», как вы описали, потому что в мире Ruby не существует такой вещи, как наследование от экземпляра.Но, сказав это, вы можете сделать некоторый взлом, чтобы получить то, что вам (я думаю) нужно, используя delegate (вы можете сделать это, потому что, как вы писали, вы используете Rails):

class A
  attr_reader :var # here, you set method to get `@var` value
  # ...
end

class B < A
  delegate :var, to: :a # here, you're setting delegator

  attr_reader :a # It's used by line above

  def initialize(a)
    @a = a
  end

  def test
    puts var
  end
end
a1 = A.new('one')
a2 = A.new('two')
b1 = B.new(a1)
b2 = B.new(a2)
b3 = B.new(a2)
b1.test
# one
b2.test
# two
b3.test
# two

Имейте в виду, что это решение, написанное с нуля, так как не существует «синтетического» способа сделать это, так как нет наследования от экземпляров в мире Ruby.

0 голосов
/ 23 сентября 2019

Одним из наиболее не рекомендуемых способов достижения этого было бы перемещение цепочки наследования в динамический модуль, например.

class InheritableState < Module
  def initialize(key)
    var_name = "@@#{key}_state".to_sym

    define_method :state do 
      return self.class.class_variable_get(var_name) if self.class.class_variable_defined?(var_name)
      self.class.class_variable_set(var_name, nil)
    end

    define_method :state= do |val|
      self.class.class_variable_set(var_name, val)
    end
  end
end

class A
end

> a, b, c = A.new, A.new, A.new
 => [#<A:0x00007fb691944338>, #<A:0x00007fb691944310>, #<A:0x00007fb6919442e8>]
> a.extend(InheritableState.new("abc")); b.extend(InheritableState.new("abc")); c.extend(InheritableState.new("def"));
 => #<A:0x00007fb6919442e8>
> [a.state, b.state, c.state]
 => [nil, nil, nil]
> a.state = "abc"
 => "abc"
> [a.state, b.state, c.state]
 => ["abc", "abc", nil]
0 голосов
/ 23 сентября 2019

Ваш код вызывает ошибку:

b = B.new
ArgumentError: wrong number of arguments (given 0, expected 1)
from (pry):14:in `initialize'

B наследует конструктор и требует аргумента в инициализации.

См. Ниже:

b = B.new("hello")
# => #<B:0x0000555b8b8c20d0 @var="hello">
b.test
# hello
# => nil

AsВы можете видеть, что ivar наследуется.

EDIT

В этом случае лучше использовать композицию, чем наследование:

class A
  def initialize(var)
    @var = var
  end
  attr_accessor :var
end

class B < A
  def initialize(a)
    @a = a
  end

  def test
    puts var
  end

  def var
    a.var
  end

  def var=(var)
    a.var=(var)
  end
end

# or with delegation
class C < A
  def initialize(a)
    @a = a
  end
  delegate :var, :var=, to: :a

  def test
    puts var
  end
end
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...