In Ruby is there a way to overload the initialize constructor?

后端 未结 7 1478
死守一世寂寞
死守一世寂寞 2020-11-29 00:58

In Java you can overload constructors:

public Person(String name) {
  this.name = name;
}
public Person(String firstName, String lastName) {
   this(firstNam         


        
相关标签:
7条回答
  • 2020-11-29 01:22

    You could use the double splat operator ** in conjunction with logical or (double pipes) || inside the initialize method to achieve the same effect.

    class Person
      def initialize(**options)
        @name = options[:name] || options[:first_name] << ' ' << options[:last_name]
      end
    end
    
    james = Person.new(name: 'James')
    #=> #<Person @name="James">
    
    jill_masterson = Person.new(first_name: 'Jill', last_name: 'Masterson')
    #=> #<Person @name="Jill Masterson">
    

    However, if a new Person is created without a first_name, then the append << operation will fail with NoMethodError: undefined method '<<' for nil:NilClass. Here is a refactored initialize method to handle this case (using strip to remove whitespace if either option is excluded).

    class Person
      def initialize(**options)
        @name = options[:name] || [ options[:first_name] , options[:last_name] ].join(' ').strip
      end
    end
    
    goldfinger = Person.new(last_name: 'Goldfinger')
    #=> #<Person @name="Goldfinger">
    
    oddjob = Person.new(first_name: 'Oddjob')
    #=> #<Person @name="Oddjob">
    

    In fact, this approach handles calling Person.new without arguments or with an unexpected key to return the new instance with @name set to an empty string:

    nameless = Person.new
    #=> <#Person @name="">
    
    middle_malcom = Person.new(middle_name: 'Malcom')
    #=> <#Person @name="">
    
    0 讨论(0)
  • 2020-11-29 01:22

    You can use konstructor gem to declare multiple constructors in Ruby and imitate overloading:

    class Person
      def initialize(name)
        @name = name
      end
    
      konstructor
      def from_two_names(first_name, last_name)
        @name = first_name + ' ' + last_name
      end
    end
    
    Person.new('John Doe')
    Person.from_two_names('John', 'Doe')
    
    0 讨论(0)
  • 2020-11-29 01:35

    The answer is both Yes and No.

    You can achieve the same result as you can in other languages using a variety of mechanisms including:

    • Default values for arguments
    • Variable Argument lists (The splat operator)
    • Defining your argument as a hash

    The actual syntax of the language does not allow you to define a method twice, even if the arguments are different.

    Considering the three options above these could be implemented with your example as follows

    # As written by @Justice
    class Person
      def initialize(name, lastName = nil)
        name = name + " " + lastName unless lastName.nil?
        @name = name
      end
    end
    
    
    class Person
      def initialize(args)
        name = args["name"]
        name = name + " " + args["lastName"] unless args["lastName"].nil?
        @name = name
      end
    end
    
    class Person
      def initialize(*args)
        #Process args (An array)
      end
    end
    

    You will encounter the second mechanism frequently within Ruby code, particularly within Rails as it offers the best of both worlds and allows for some syntactic sugar to produce pretty code, particularly not having to enclose the passed hash within braces.

    This wikibooks link provides some more reading

    0 讨论(0)
  • 2020-11-29 01:38

    checkout functional-ruby gem which is inspired by Elixir pattern matching features.

       class Person
         include Functional::PatternMatching
    
         defn(:initialize, String) { |name| 
           @name = name 
         }
    
         defn(:initialize, String, String) {|first_name, last_name| 
          @name = first_name + ' ' + last_name
         }
       end
    
    0 讨论(0)
  • 2020-11-29 01:41

    I tend to do

    class Person
      def self.new_using_both_names(first_name, last_name)
        self.new([first_name, last_name].join(" "))
      end
    
      def self.new_using_single_name(single_name)
        self.new(single_name)
      end
    
      def initialize(name)
        @name = name
      end
    end
    

    But I don't know if this is the best approach.

    0 讨论(0)
  • 2020-11-29 01:45
    class StatementItem
      attr_reader :category, :id, :time, :amount
    
      def initialize(item)
        case item
        when Order
          initialize_with_order(item)
        when Transaction
          initialize_with_transaction(item)
        end
      end
    
      def valid?
        !(@category && @id && @time && @amount).nil?
      end
    
      private
        def initialize_with_order(order)
          return nil if order.status != 'completed'
          @category = 'order'
          @id = order.id
          @time = order.updated_at
          @amount = order.price
        end
    
        def initialize_with_transaction(transaction)
          @category = transaction.category
          @id = transaction.id
          @time = transaction.updated_at
          @amount = transaction.amount
        end
    
    end
    
    0 讨论(0)
提交回复
热议问题