Ruby中的“父”,“子”类(不是继承)

时间:2016-11-15 18:17:07

标签: ruby object-oriented-analysis

假设我有两节课。一个班级,“父母”,有许多另一个班级“孩子”。这不是继承,我不希望父方法对子对象起作用。我想要的是子对象能够引用父对象,从中获取变量(child.parent.var)并调用修改父对象的父方法(child.parent.update)。

我想要一个对象(可以被认为是一个孩子而不是孩子因为这不是继承)在初始化时传递对另一个对象的引用。我将它与父数据库中的父子关系进行比较,我们将信息存储在父数据库中,因此我们不必将其复制到每个子项上。

示例:

class Parent
    attr_accessor :var

  def initialize(num)
    @var = num
  end

  def increase
    @var += 1
  end
end

class Child
    attr_accessor :var, :parent

    def initialize(parent, num)
        @parent = parent
        @var = num
    end

    def sum
        @parent.increase
        @parent.var + var
    end
end

parent1 = Parent.new(1)

child1 = Child.new(parent1, 2)
child2 = Child.new(parent1, 3)

child1.parent.increase # update the parent variable
child2.parent.var      # get the parent variable

上面的代码确实有效,但有没有更好的(更简洁,或更多ruby-esq)方法来实现这个目标?

非常感谢你的帮助/想法。

1 个答案:

答案 0 :(得分:3)

这基本上是应该如何完成的:)虽然有一些可能的改进,取决于你真正想要实现的目标。

目前,您的Child个实例在其外部接口上公开了对父级的访问权限(通过公共parent访问者)。这通常违反了Law of Demeter,它指出对象应该只与他们的直接邻居交谈。从这个意义上说,parent在通过子对象访问时是一个陌生人。

您可以通过隐藏父对象来改进设计:

class Child
  extend Forwardable

  def_delegator :@parent, :var, :parent_var
  def_delegator :@parent, :increase

  attr_accessor :var

  def initialize(parent, num)
    @parent = parent
    @var = num
  end

  def sum
    @parent.increase
    @parent.var + var
  end
end

在这里,我们使用Ruby的Forwardable模块来提供对客户端父级方法的访问。这使得这些方法成为您的Child类的单个公共接口的一部分。

parent = Parent.new(1)

child = Child.new(parent, 2)

child.var
# => 2
child.parent_var
# => 1
child.increase
# => 2
parent.var
# => 2
# ^^^^ the increase method was called on the parent object

从外部来看,方法转发并不重要,您可以稍后更改它而不会影响您的外部接口。

第二个改进可能是扩展您的Parent课程以直接生成孩子:

class Parent
  # ...

  def child(num)
    Child.new(self, num)
  end
end

这通常称为Factory Method,即构建其他对象的方法。通过这种方式,您可以隐藏构建Child对象并将其附加到父对象的复杂性。

您可以将其称为

parent = Parent.new(1)
child = parent.child(2)