Have a different public constructor than a private one - ruby

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?

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

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.

Related

Ruby class, arguments being passed around methods, instance variable?

I'm having this problem which I think it might be a code smell, I have a class that receives an argument in its initialiser and contains one public and several private methods - everything normal. Example:
class Foo
def initialize(a)
#a = a
end
def apply?(examples)
foo_1(examples)
end
private
def foo_1(examples)
foo_2(examples) ? 1 : 2
end
def foo_2(examples)
examples.size > #a
end
end
My problem here, is 'examples' that is received by the public method being carried around over and over the private methods, it doesn't look pretty and it seems like a code smell, what's the best approach here? Make it an instance variable inside the public method?
Thanks
Yes, this may be considered a code smell if the number of private methods accepting examples is bigger than 1-2.
One thing to consider would be to extract a class to represent the rule here.
For example:
class Foo
def initialize(a)
#a = a
end
def apply?(examples)
size_rule_applies?(examples) ? 1 : 2
end
private
def size_rule_applies?(examples)
SizeRule.new(#a, examples).apply?
end
class SizeRule
def initialize(a, examples)
#a = a
#examples = examples
end
def apply?
#examples.size > #a
end
end
end
I wouldn't make the examples an instance variable of the Foo class as there's a risk it would persist in memory between the calls to that object. I've seen bugs like that.
If examples changes dynamically then making it instance variable is not an option. You'll either need to instantiate Foo for every examples or you'll end up having mutable Foo where examples itself is changing, which is also not good.
Your code looks fine. The only thing that concerns me is that one method depends on another. It's usually not a big deal, but this would look better to me:
def apply?(examples)
foo_1(examples) && foo_2(examples)
end
Another option is to use block:
class Foo
def initialize(a)
#a = a
end
def apply?(examples)
foo_1 { examples }
end
private
def foo_1
v = foo_2 { yield.size }
v ? 1 : 2
end
def foo_2
yield > #a
end
end
Since the class Foo does nothing else presently than just evaluate .apply?(examples) I will advice examples be added to the initializer and made an instance variable. This is simpler, more efficient and more obvious.
class Foo
def initialize(a, examples)
#a = a
#examples = examples
end
def apply?
foo_1
end
private
def foo_1
foo_2 ? 1 : 2
end
def foo_2
#examples.size > #a
end
end

How should I define an instance variable in Ruby whose processing algorithm is defined in a super class?

In short: I want to define the algorithm in the superclass which inherits into all subclasses, but I want to define the data (on which the algorithm operates) in the subclasses as instance variables, which come into being when I call the "new" method of the given classes. What is the standard way for doing this in Ruby?
My solution is (but I am not exactly sure this is the right way):
class A
attr_accessor :var
def initialize
#var=nil #I dont know the actual value, it will be defined only in the more specific subclasses.
end
def process_data
puts #var #simply puts it out
end
end
#in my program all further classes are inherited form class A, the processing facility is inherited, only the data varies.
class B < A
attr_accessor :var
def initialize
#var=10 #specific value for class B which is always 10, no need for example b=B.new(20)
end
end
class C < A
attr_accessor :var
def initialize
#var=20 #specific value for class C which is always 20, no need for example c=C.new(20)
end
end
b=B.new
b.process_data #needs to print 10
c=C.new
c.process_data #needs to print 20
What you have works. There's just some unneeded stuff in there:
Instance variables evaluate to nil if they are uninitialized and they spring into existence as soon as they are used, so your A#initialize method is unnecessary.
You override the A#var and A#var= methods in B and C with methods that do the exact same thing. There is no need for that, just get rid of the calls to attr_accessor in the definition of B and C.
You create var and var= accessor methods but you never use them. Either get rid of the call to attr_accessor or (preferably) use the accessor methods, i.e. use self.var = in initialize and puts var in process_data.
class A
attr_accessor :var
def process_data
puts var #simply puts it out
end
end
#in my program all further classes are inherited form class A, the processing facility is inherited, only the data varies.
class B < A
def initialize
self.var = 10 #specific value for class B which is always 10, no need for example b=B.new(20)
end
end
class C < A
def initialize
self.var = 20 #specific value for class C which is always 20, no need for example c=C.new(20)
end
end
b = B.new
b.process_data #needs to print 10
c = C.new
c.process_data #needs to print 20
[Note: your coding style was also off. Indentation is 2 spaces in Ruby, not 3.]
However, if the value of #var is always the same for all instances of B, then why do you need multiple instances of B?
Why not something like this:
class A
attr_accessor :var
def initialize(val)
self.var = val
end
def process_data
puts var #simply puts it out
end
end
b = A.new(10)
b.process_data #needs to print 10
c = A.new(20)
c.process_data #needs to print 20
You are doing it in almost the right way. A minor point is that your A::initialize definition is redundant for two reasons:
An instance variable is initialized to nil automatically.
The initialize methods in the subclasses will override A::initialize when the subclass instances are created.
Also, the attr_accessor calls in the subclasses are redundant.
Now I seem to get what you wanted. You can use class-level instance variables.
class A
def initialize
#var=self.class.instance_variable_get(:#var)
end
def process_data
puts #var
end
end
class B < A
#var = 10
end
class C < A
#var = 20
end
B.new.process_data # => 10
C.new.process_data # => 20

customizing ruby .new operator

Let's say I have a class Foo and the constructor takes 2 parameters.
Based on these parameters the initialize method does some heavy calculations and stores them as variables in the instance of the class. Object created.
Now I want to optimize this and create a cache of these objects. When creating a new Foo object, I want to return a existing one from the cache if the parameters match. How can I do this?
I currently have a self.new_using_cache(param1, param2), but I would love to have this integrated in the normal Foo.new().
Is this possible in any way?
I can also deduct that using .new() combined with a cache is not really syntactical correct.
That would mean that the method should be called new_or_from_cache().
clarification
It's not just about the heavy calculation, it's also preferred because of limiting the amount of duplicate objects. I don't want 5000 objects in memory, when I can have 50 unique ones from a cache. So I really need to customize the .new method, not just the cached values.
class Foo
##cache = {}
def self.new(value)
if ##cache[value]
##cache[value]
else
##cache[value] = super(value)
end
end
def initialize(value)
#value = value
end
end
puts Foo.new(1).object_id #2148123860
puts Foo.new(2).object_id #2148123820 (different from first instance)
puts Foo.new(1).object_id #2148123860 (same as first instance)
You can actually define self.new, then call super if you actually want to use Class#new.
Also, this totally approach prevents any instantiation from ever occurring if a new instance isn't actually needed. This is die to the fact the initialize method doesn't actually make the decision.
Here's a solution I came up with by defining a generic caching module. The module expects your class to implement the "retrieve_from_cache" and "store_in_cache" methods. If those methods don't exist, it doesn't attempt to do any fancy caching.
module CacheInitializer
def new(*args)
if respond_to?(:retrieve_from_cache) &&
cache_hit = retrieve_from_cache(*args)
cache_hit
else
object = super
store_in_cache(object, *args) if respond_to?(:store_in_cache)
object
end
end
end
class MyObject
attr_accessor :foo, :bar
extend CacheInitializer
#cache = {}
def initialize(foo, bar)
#foo = foo
#bar = bar
end
def self.retrieve_from_cache(foo, bar)
# grab the object from the cache
#cache[cache_key(foo, bar)]
end
def self.store_in_cache(object, foo, bar)
# write back to cache
#cache[cache_key(foo, bar)] = object
end
private
def self.cache_key(foo, bar)
foo + bar
end
end
Something like this?
class Foo
##cache = {}
def initialize prm1, prm2
if ##cache.key?([prm1, prm2]) then #prm1, #prm2 = ##cache[[prm1, prm2]] else
#prm1 = ...
#prm2 = ...
##cache[[prm1, prm2]] = [#prm1, #prm2]
end
end
end
Edited
To not create an instance when the parameters are the same as before,
class Foo
##cache = {}
def self.new prm1, prm2
return if ##cache.key?([prm1, prm2])
#prm1 = ...
#prm2 = ...
##cache[[prm1, prm2]] = [#prm1, #prm2]
super
end
end
p Foo.new(1, 2)
p Foo.new(3, 4)
p Foo.new(1, 2)
# => #<Foo:0x897c4f0>
# => #<Foo:0x897c478>
# => nil
You could use a class-level instance variable to store results from previous object instantiations:
class Foo
#object_cache = {}
def initialize(param1, param2)
#foo1 = #object_cache[param1] || #object_cache[param1] = expensive_calculation
#foo2 = #object_cache[param2] || #object_cache[param2] = expensive_calculation
end
private
def expensive_calculation
...
enf
end
As you probably know you have reinvented the factory method design pattern and it's a perfectly valid solution using your name for the factory method. In fact, it's probably better to do it without redefining new if anyone else is going to have to understand it.
But, it can be done. Here is my take:
class Test
##cache = {}
class << self
alias_method :real_new, :new
end
def self.new p1
o = ##cache[p1]
if o
s = "returning cached object"
else
##cache[p1] = o = real_new(p1)
s = "created new object"
end
puts "%s (%d: %x)" % [s, p1, o.object_id]
o
end
def initialize p
puts "(initialize #{p})"
end
end
Test.new 1
Test.new 2
Test.new 1
Test.new 2
Test.new 3
And this results in:
(initialize 1)
created new object (1: 81176de0)
(initialize 2)
created new object (2: 81176d54)
returning cached object (1: 81176de0)
returning cached object (2: 81176d54)
(initialize 3)

Protected and Private methods

I'm reading through Beginning Ruby and I'm stuck at the part about private and protected methods. This is a newbie question, I know. I searched through SO for a bit but I couldn't manage to find a clear and newbie-friendly explanation of the difference between private and protected methods.
The book gives two examples, the first one for private methods:
class Person
def initialize(name)
set_name(name)
end
def name
#first_name + ' ' + #last_name
end
private
def set_name(name)
first_name, last_name = name.split(/\s+/)
set_first_name(first_name)
set_last_name(last_name)
end
def set_first_name(name)
#first_name = name
end
def set_last_name(name)
#last_name = name
end
end
In this case, if I try
p = Person.new("Fred Bloggs")
p.set_last_name("Smith")
It will tell me that I can't use the set_last_name method, because it's private. All good till there.
However, in the other example, they define an age method as protected and when I do
fred = Person.new(34)
chris = Person.new(25)
puts chris.age_difference_with(fred)
puts chris.age
It gives an error:
:20: protected method 'age' called for #<Person:0x1e5f28 #age=25> (NoMethodError)
I honestly fail to see the difference between the private and protected methods, it sounds the same to me. Could someone provide me with a clear explanation so I'll never get confused about this again?
I'll provide the code for the second example if necessary.
protected methods (or atributes) can only be used by the classes that inherit the class with protected methods (or atributes).
a d
\ \
b e
\
c
If the class a have a protected method, this can be used by b and c, but can't be used by d or e.
Note: Ascii art diagram for inherit in ruby classes.
class A
public
def f obj
obj.c
end
def g obj
obj.b
end
def call_b
b
end
private
def b
puts "Hi!_b"
end
protected
def c
puts "Hi!_c"
end
end
a = A.new
b = A.new
a.f(b) # Hi!_c
a.g(b) # inj.rb:7:in `g': private method `b' called for #<A:0xb76df4cc> (NoMethodError)
a.call_b # Hi!_b
In this case, the method f can 'see' the protected method because its of the same class (or an inherited one), but the private method encapsulate(hides) the 'b' method of all the cases, except if this is called inside of his class (by another accesible method (in this case, the method call_b)).

Ruby- read the value of a variable in another class?

I have something like the following
class A
def initialize
#var = 0
end
def dosomething
#var+=1
end
end
class B < A
def initialize
super
end
def func
puts #var
end
end
The problem is when I call
a = A.new
a.dosomething
b = B.new
the value which #var returns is 0 how would I change my code so it would return the "new" value of var (1)?
Quick answer, for if you actually understand Classes, Inheritance and Objects : replace #var (an instance variable, and therefore different in a and b) with ##var (a class variable, and therefore the same in all instances of class A).
Otherwise, your question indicates you have a fundamental misunderstanding of what's going on with classes, objects and inheritance.
Your code does the following:
Defines a class, called A. This is essentially a blueprint from which you can create objects.
Declares that when an object of type A is created, that object should be given it's own private copy of an attribute, called var, which is set to 0.
Declares that objects of type A can be asked to dosomething, which increases the value of that object's var by 1.
Defines a class called B, which is a special case of an A
Therefore, in your second snippet, you create an object a, which is an A. It has its own attribute called var, which is set to 0 and then incremented. You then create b, which is a B (and is therefore also an A). b has its own attribute called var, separate from a's var, which is set to 0.
Variables like #var are called instance variables because they are unique to every instance of a class. You've created two separate instances, a and b, and they have their own instance variables.
You can use class variables if you want to see them in your subclass:
class A
##var = 0
def dosomething
##var += 1
end
end
class B < A
def func
puts ##var
end
end
You can also use class-level accessor methods:
class A
class << self
attr_accessor :var
def dosomething(n)
self.var = n
end
end
end
class B < A
def func
puts A.var
end
end
irb(main):031:0> A.dosomething(5)
=> 5
irb(main):032:0> b = B.new
=> #<B:0x2b349d>
irb(main):033:0> b.func
5
Note that inheritance is not needed for this to work.
You could use class variables:
class A
##var = 0
def dosomething
##var += 1
end
end
class B < A
def func
puts ##var
end
end
a = A.new
a.dosomething
a.dosomething
b = B.new
b.func # => 2
#var is an instance variable, each instance of this class has its own value of this variable. So it is correct that b does return 0, since it is a different instance compared to a.
To update the value in b use:
b = B.new
b.dosomething
Here is more information on instance variables.
Or if you want a class variable (ie a variable which is the same in all instances of that class), use ##var. Then your given example works.
Here is some more information on class variables.
Which solution you need, depends on your application.
You need to use class variables, not instance variables. Try something like this:
class A
##var = 0
def dosomething
##var+=1
end
end
class B < A
def func
puts ##var
end
end

Resources