Codoscope
Codoscope

Reputation: 944

Have a different public constructor than a private one

I read that it is not possible to have several constructors for a class. So the following code won't work:

class C
    def initialize x
        initialize x,0
    end

    # Some methods using the private constructor…

    def foo
        # …
        bar = C.new 3,4
        # …
    end

    private

    def initialize x,y
        @x = x
        @y = y
    end
end

I have thought about replacing the public constructor by a static method, but that would prevent other classes to extend C. I also have thought about making a private post-initializing method:

class C
    def initialize x
        post_init x,0
    end

    # Some methods using the private constructor…

    def foo
        # …
        bar = C.new baz
        bar.post_init 3,4
        # …
    end

    private

    def post_init x,y
        @x = x
        @y = y
    end
end

But here, the post_init is called twice, which is not a good thing.

Is there a way to give a public constructor, while having in private a more complete way to make a new instance? If not, what is the best way to do something similar?

Upvotes: 0

Views: 106

Answers (2)

snovity
snovity

Reputation: 1173

I guess this would do what you expect.

class C
  def initialize(x, y = 0)
    @x = x
    @y = y
  end

  def self.with_position
    new(3, 4)
  end
end

c1 = C.new(5)
c2 = C.with_position

If you want to prohibit setting y by anyone from outside the class, you can use some private method behind the scenes (as you suggested) and konstructor gem

class C
  def initialize(x)
    set_coords(x, 0)
  end

  konstructor
  def with_position
    set_coords(3, 4)
  end

  private

  def set_coords(x, y)
    @x = x
    @y = y
  end
end

c1 = C.new(5)
c2 = C.with_position

Upvotes: 1

max pleaner
max pleaner

Reputation: 26758

A simple way is to accept options for initialize, you can have an if statement in there that covers the private or public cases.

Ruby doesn't really have this concept of 'private class' in a simple way such as saying 'private'.

You can see How to I make private class constants in Ruby for a way to make a private constant (since classes are constants). You'd make a class method that returns an anonymous class (Class.new do ... end). Then mark that class method private with private_class_method.

A better solution would be to make two classes with different initializes. Common functionality could be in a separate class or module. If it's a class, then the way to include them in the 'public/private' classes would be inheritance. If it's a module, then you'd include/extend.

Upvotes: 1

Related Questions