【问题标题】:Ruby - initialize inheritance, super with only certain arguments?Ruby - 初始化继承,只有某些参数?
【发布时间】:2016-09-27 06:45:01
【问题描述】:

我最近一直在玩 Ruby,但我似乎找不到我的问题的答案。

我有一个类和一个子类。类有一些initialize 方法,子类有自己的initialize 方法,该方法应该从它继承一些(但不是全部)变量,并将自己的变量添加到子类对象中。

我的Person@name@age@occupation

我的Viking 应该有一个@name@age,它继承自Person,另外还有一个@weaponPerson 没有。 Viking 显然不需要任何@occupation,也不应该有。

# doesn't work
class Person
  def initialize(name, age, occupation)
    @name = name
    @age = age
    @occupation = occupation
  end
end

class Viking < Person
  def initialize(name, age, weapon)
    super(name, age) # this seems to cause error
    @weapon = weapon
  end
end

eric = Viking.new("Eric", 24, 'broadsword') 
# ArgError: wrong number of arguments (2 for 3)

您可以通过以下方式使其工作,但两种解决方案都不适合我

class Person
  def initialize(name, age, occupation = 'bug hunter')
    @name = name
    @age = age
    @occupation = occupation
  end
end

class Viking < Person
  def initialize(name, age, weapon)
    super(name, age)
    @weapon = weapon
  end
end

eric = Viking.new("Eric", 24, 'broadsword') 
# Eric now has an additional @occupation var from superclass initialize


class Person
  def initialize(name, age, occupation)
    @name = name
    @age = age
    @occupation = occupation
  end
end

class Viking < Person
  def initialize(name, age, occupation, weapon)
    super(name, age, occupation)
    @weapon = weapon
  end
end

eric = Viking.new("Eric", 24, 'pillager', 'broadsword')
# eric is now a pillager, but I don't want a Viking to have any @occupation

问题是

  1. 这是设计使然,我想犯一些违反 OOP 原则的大罪吗?

  2. 我如何让它以我想要的方式工作(最好没有任何疯狂的复杂元编程技术等)?

【问题讨论】:

    标签: ruby inheritance initialization


    【解决方案1】:

    super 如何处理参数

    关于参数处理,super 关键字可以以三种方式表现:

    当不带参数调用时,super 会自动将调用它的方法(在子类中)接收到的任何参数传递给超类中的相应方法。

    class A
      def some_method(*args)
        puts "Received arguments: #{args}"
      end
    end
    
    class B < A
      def some_method(*args)
        super
      end
    end
    
    b = B.new
    b.some_method("foo", "bar")     # Output: Received arguments: ["foo", "bar"]
    

    如果使用空括号(空参数列表)调用,则不会将任何参数传递给超类中的相应方法,无论调用 super 的方法(在子类上)是否收到任何参数。

    class A
      def some_method(*args)
        puts "Received arguments: #{args}"
      end
    end
    
    class B < A
      def some_method(*args)
        super()  # Notice the empty parentheses here
      end
    end
    
    b = B.new
    b.some_method("foo", "bar")     # Output: Received arguments: [ ]
    

    当使用显式参数列表调用时,它将这些参数发送到超类中的相应方法,而不管调用 super 的方法(在子类上)是否收到任何参数。

    class A
      def some_method(*args)
        puts "Received arguments: #{args}"
      end
    end
    
    class B < A
      def some_method(*args)
        super("baz", "qux")  # Notice that specific arguments were passed here
      end
    end
    
    b = B.new
    b.some_method("foo", "bar")     # Output: Received arguments: ["baz", "qux"]
    

    【讨论】:

      【解决方案2】:

      是的,你犯了大罪(很明显,你知道它,因为你在问它)。 :)

      你违反了Liskov substitution principle(可能还有其他一些命名或未命名的规则)。

      您可能应该提取另一个类作为公共超类,它不包含occupation。这将使一切变得更加清晰和干净。

      【讨论】:

      • LSP 不适用于构造函数,因为你不以多态方式使用它们(即你总是知道你在处理哪种类型)。见stackoverflow.com/questions/5490824/…
      • 你可能是对的。然而,至少在 Ruby 中,你并不总是知道你在处理哪种类型,因为类也是对象。所以,这个问题的最终答案取决于构造函数的使用方式。
      • 确实如此,尤其是对于一些动态工厂。 :)
      【解决方案3】:

      实际上,这只是 2 种方式 - 自动包含所有属性(仅包含 super 一词)和 super ,您可以在其中选择要传递的参数。执行 super() 不会选择传递给父类的参数。

      编辑:这是对上述评论的评论,但当某人是新人时,他们不允许 cmets...哦,好吧。

      【讨论】:

        【解决方案4】:

        您可以避免为Person Class 中的occupation 参数设置默认值,只需在super() 中传递occupationnil 参数即可。这允许 你用你的 3 个参数调用 Viking.new (name, age, weapon) 没有 必须额外考虑occupation

        class Person
          def initialize(name, age, occupation)
            @name = name
            @age = age
            @occupation = occupation
          end
        end
        
        class Viking < Person
          def initialize(name, age, weapon)
            super(name, age, nil)
            @weapon = weapon
          end
        end
        
        eric = Viking.new("Eric", 24, 'broadsword') 
        p eric
        

        输出维京人:0x00007f8e0a119f78 @name="Eric", @age=24, @occupation=nil, @weapon="broadsword"

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2011-12-03
          • 1970-01-01
          • 1970-01-01
          • 2013-12-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多