65
class A
private
  def initialize
    puts "wtf?"
  end
end

A.new #still works and calls initialize

and

class A
private
  def self.new
    super.new
  end
end

doesn't work altogether

So what's the correct way? I want to make new private and call it via a factory method.

Andrew Grimm
  • 78,473
  • 57
  • 200
  • 338
Leonid Shevtsov
  • 14,024
  • 9
  • 51
  • 82
  • I'm not sure what you want is possible, but even if it was, in theory, you wouldn't be able to call it from a factory method because it would be private. Are you trying to create a singleton? Or do an inversion-of-control pattern? – Matt Oct 14 '09 at 16:27
  • Restricting constructor access is helpful when an instance has state that should not be shared across operations, and you want to prevent the caller from accidentally using an object for more than one operation. A file importer or parser, for example, where you could have a static method `parse` or `import` (or `call`). The instance is an implementation detail not relevant to the caller. – Keith Bennett Aug 31 '20 at 16:59
  • @Matt Maybe I don't understand you completely, but I don't think that's true. If you paste this into `irb`, and then input `A.create`, an instance is created: `class A; private_class_method :new; def self.create; self.new; end; end`. – Keith Bennett Aug 31 '20 at 17:02

3 Answers3

100

Try this:

class A
  private_class_method :new
end

More on APIDock

adurity
  • 1,112
  • 1
  • 8
  • 5
  • 10
    In case you are looking to implement a Singleton class (the only reason I can think of wanting a private constructor), Ruby will do it for you. http://apidock.com/ruby/Singleton – adurity Oct 14 '09 at 16:37
  • 2
    And even then someone could do A.send(:new). (BTW, shouldn't "class" be lower case?) – Andrew Grimm Oct 15 '09 at 06:58
  • Yes it should. Corrected now. – adurity Oct 16 '09 at 21:47
  • It's kind of strange that `private_class_method`, `protected_class_method`, and `public_class_method` are public methods! – Kelvin Oct 03 '12 at 21:54
  • 25
    @adurity, you might also want to have specialized factory methods. – Papipo Jun 28 '14 at 18:43
  • 12
    You might want a private constructor to allow the class to create instances of itself in ways you wouldn't trust external callers to - e.g. directly assigning to protected variables that should be immutable to or hidden from other initializers – Orphid May 15 '15 at 18:07
  • The flyweight pattern is a lot like the singleton pattern - you might have a set of immutable objects that you don't want to create a lot of instances of but want to use over and over again. So you have a "get_instance(args)" class method, which checks the cache, and uses the cached version. @adurity – Thomas Andrews Sep 13 '15 at 16:44
  • 9
    @adurity: Maybe you want to implement a strict way to create the object. For example: Quote.for_truck(truck) and Quote.for_car(car) but you don't want to allow people building Quotes from scratch. – Lomefin Jul 04 '16 at 02:47
14

To shed some light on the usage, here is a common example of the factory method:

class A
  def initialize(argument)
    # some initialize logic
  end

  # mark A.new constructor as private
  private_class_method :new

  # add a class level method that can return another type
  # (not exactly, but close to `static` keyword in other languages)
  def self.create(my_argument)
     # some logic
     # e.g. return an error object for invalid arguments
     return Result.error('bad argument') if(bad?(my_argument))

     # create new instance by calling private :new method
     instance = new(my_argument)
     Result.new(instance)
  end
end

Then use it as

result = A.create('some argument')    

As expected, the runtime error occurs in the case of direct new usage:

a = A.new('this leads to the error')
Artur A
  • 7,115
  • 57
  • 60
13

The second chunk of code you tried is almost right. The problem is private is operating in the context of instance methods instead of class methods.

To get private or private :new to work, you just need to force it to be in the context of class methods like this:

class A
  class << self
    private :new
  end
end

Or, if you truly want to redefine new and call super

class A
  class << self
    private
    def new(*args)
      super(*args)
      # additional code here
    end
  end
end

Class-level factory methods can access the private new just fine, but trying to instantiate directly using new will fail because new is private.

Nathan
  • 7,816
  • 8
  • 33
  • 44
  • Upvoted because this is an entirely valid way of making a class private method. I didn't know about opening classes' classes six years ago. :) – Leonid Shevtsov Oct 08 '15 at 19:58