0

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?

Community
  • 1
  • 1
Codoscope
  • 892
  • 1
  • 10
  • 18
  • I don't really understand why would you want to do that, but what you came up with seems to fit your purpose, except `post_init` in your code is a class method, however you're calling it from instance methods and this won't work. – Nikita Misharin Dec 14 '16 at 23:09
  • I made a mistake about the class method, it should be an instance method. I updated the post in consequence. – Codoscope Dec 14 '16 at 23:13
  • Have you thought about directly changing `@y`, where you need? it looks like a more common and straight forward approach – Nikita Misharin Dec 14 '16 at 23:55
  • Actually this is a simplified code to explain my problem. You can also considerate that there are no arguments for the constructors but that I want to give two different behaviors for a private instantiation than for a public one. – Codoscope Dec 15 '16 at 00:00

2 Answers2

1

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.

Community
  • 1
  • 1
max pleaner
  • 26,189
  • 9
  • 66
  • 118
  • `since classes are constants` I think class' name is a constant pointing to a class, but class itself is nit a constant :) – Andrey Deineko Dec 15 '16 at 06:16
  • I don't understand the first solution. How should the returned anonymous class be useful? Does it inherit from the main class? The second solution seems clearer, but it is very bad for the user because he has to deal with two classes for no apparent reasons. You have a point nonetheless so +1. – Codoscope Dec 15 '16 at 21:46
1

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
snovity
  • 488
  • 4
  • 11