Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ruby - initialize inheritance, super with only certain arguments?

I've been playing around with Ruby as of late and I can't seem to find the answer to my question.

I have a class and a subclass. Class has some initialize method, and subclass has its own initialize method that is supposed to inherit some (but not all) variables from it and additionally add its own variables to the subclass objects.

My Person has @name, @age and @occupation.

My Viking is supposed to have a @name and @age which it inherits from Person, and additionally a @weapon which Person doesn't have. A Viking obviously doesn't need any @occupation, and shouldn't have one.

# 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)

You can make it work in the following ways, but neither solution appeals to me

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

The question is either

  1. is it by design and I want to commit some Cardinal Sin against OOP principles?

  2. how do I get it to work the way I want to (preferably without any crazy complicated metaprogramming techniques etc)?

like image 968
blob Avatar asked May 29 '16 10:05

blob


People also ask

What is the difference between super and super () in Ruby?

When you call super with no arguments, Ruby sends a message to the parent of the current object, asking it to invoke a method with the same name as where you called super from, along with the arguments that were passed to that method. On the other hand, when called with super() , it sends no arguments to the parent.

How do I use super keyword in Ruby?

The super keyword can be used to call a method of the same name in the superclass of the class making the call. It passes all the arguments to parent class method.

What does .NEW do in Ruby?

Ruby | Enumerator::new function The new function in Ruby is used to create a new Enumerator object, which can be used as an Enumerable. Here, Enumerator is an object. Parameters: This function does not accept any parameters. Returns: the new set of values.

What is single inheritance in Ruby?

In Ruby, a class can inherit from one other class. This is called single inheritance. Some languages have support for multiple inheritance, which means a class can inherit from multiple classes.


2 Answers

How super handles arguments

Regarding argument handling, the super keyword can behave in three ways:

When called with no arguments, super automatically passes any arguments received by the method from which it's called (at the subclass) to the corresponding method in the superclass.

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"]

If called with empty parentheses (empty argument list), no arguments are passed to the corresponding method in the superclass, regardless of whether the method from which super was called (on the subclass) has received any arguments.

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: [ ]

When called with an explicit argument list, it sends those arguments to the corresponding method in the superclass, regardless of whether the method from which super was called (on the subclass) has received any arguments.

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"]
like image 173
BrunoF Avatar answered Oct 26 '22 22:10

BrunoF


Yes, you are committing a Cardinal Sin (obviously, you are aware of it, since you are asking about it). :)

You are breaking Liskov substitution principle (and probably some other named or unnamed rules).

You should probably extract another class as a common superclass, which does not contain occupation. That will make everything much clearer and cleaner.

like image 28
Mladen Jablanović Avatar answered Oct 26 '22 20:10

Mladen Jablanović