如何最好地分解出通用类方法

问题描述 投票:1回答:1

我正在用Ruby构建内存中的实例模型。有很多类,每个类都由该类上的类方法实例化和管理。有很多这样的类方法,例如列出所有实例,检索所有实例,等等。

这些方法的代码在所有类中都是通用的,不需要考虑这些类的任何特殊性。因此,我希望该代码可以存在于一个共同的地方。请参见下面的list方法。我的问题:如何最好地实现这一目标。

class A
    attr_reader :value
    @@instances = []

    def initialize(value:)
        @value = value; @@instances << self
    end

    def self.list
        @@instances.each { |i| puts "#{i.value}"}
    end
end

class B
    attr_reader :value
    @@instances = []

    def initialize(value:)
        @value = value; @@instances << self
    end

    def self.list
        @@instances.each { |i| puts "#{i.value}"}
    end
end

A.new(value: '100')
A.new(value: '101')
B.new(value: '200')
B.new(value: '201')

A.list
B.list

理想情况下,我只定义一次list方法。我也尝试将其移至超类:

class Entity
    def self.list
        @@instances.each { |i| puts "AB: #{i.value}"}
    end
end

class A < Entity
    attr_reader :value
    @@instances = []

    def initialize(value:)
        @value = value; @@instances << self
    end
end

class B < Entity
    attr_reader :value
    @@instances = []

    def initialize(value:)
        @value = value; @@instances << self
    end
end

...但是,正如人们期望的那样,超类无法访问其子类的@@instances数组。将@@instances数组移到超类会导致该数组对于all类是通用的,这不是我所需要的。

ruby oop class-method
1个回答
1
投票

您需要进行的主要更改是使用类实例变量而不是类变量。出于解释的原因,应谨慎使用here类变量;类实例变量通常是一个更好的选择,这个问题很好地说明了这一点。

class Entity
  attr_reader :value

  class << self
    attr_reader :ins
  end

  def self.inherited(klass)
    klass.instance_variable_set(:@ins, [])
  end       

  def initialize(value:)
    @value = value
    self.class.ins << self
  end

  def self.list
    @ins.each { |i| puts "#{i.value}"}
  end
end

class A < Entity; end
class B < Entity; end

A.new(value: '100')
  #=> #<A:0x00005754a59dc640 @value="100"> 
A.new(value: '101')
  #=> #<A:0x00005754a59e4818 @value="101"> 
A.list
  # 100
  # 101

B.new(value: '200')
  #=> #<B:0x00005754a59f0910 @value="200"> 
B.new(value: '201')
  #=> #<B:0x00005754a59f8b88 @value="201"> 
B.list
  # 200
  # 201

我为@ins单例类 1:中的类实例变量Entity定义了getter

class << self
  attr_reader :ins
end

创建Entity的子类时,回调方法 Class::inheritedEntity上执行,将已创建的类作为参数传递。 inherited为创建的类创建并初始化(为空数组)类实例变量@ins

不使用回调方法的另一种方法如下。

class Entity
  attr_reader :value

  class << self
    attr_accessor :ins
  end

  def initialize(value:)
    @value = value
    (self.class.ins ||= []) << self
  end

  def self.list
    @ins.each { |i| puts "#{i.value}"}
  end
end

片段:

(self.class.ins ||= [])

如果@ins等于@ins,则将nil设置为空数组。如果在创建@ins之前已对其进行引用,则返回nil,因此无论哪种方式,@ins都将等于[]。为了执行此语句,我需要将attr_reader :ins更改为attr_accessor :ins以执行分配@ins = [](尽管我可以改用instance_variable_set)。

[请注意,如果我将@ins = []行添加到Entity(例如第一行),则在创建子类时将为每个子类创建实例变量@ins,但是该实例变量将没有初始化为空数组,因此该行将无用。

1。或者,可以写singleton_class.public_send(:attr_reader, :ins)

© www.soinside.com 2019 - 2024. All rights reserved.