Reputation: 1437
I'm trying to convert Celsius temperatures to Fahrenheit and vice versa. As in the following, instance variable @temperature
is defined in the methods celsius=
and fahrenheit=
respectively.
class Temperature
def self.ctof(temp)
(temp * 9 / 5.0) + 32
end
def self.ftoc(temp)
(temp - 32) * (5 / 9.0)
end
def initialize(options)
if options[:f]
self.fahrenheit = options[:f]
else
self.celsius = options[:c]
end
end
def fahrenheit=(temp)
@temperature = self.class.ftoc(temp)
end
def celsius=(temp)
@temperature = temp
end
def in_fahrenheit
self.class.ctof(@temperature)
end
def in_celsius
@temperature
end
end
It is confusing to me because I've never seen instance variables defined outside of the initialize method. I'm hoping someone can help me understand what is going on here.
Upvotes: 0
Views: 672
Reputation: 114178
It is perfectly fine to set instance variables outside of initialize
. This is exactly what setters do. You probably already know attr_accessor
– when calling attr_accessor :foo
, it creates a getter foo
and a setter foo=
for the instance variable @foo
, i.e. two methods equivalent to:
def foo
@foo
end
def foo=(value)
@foo = value
end
In your code, in_celsius
and celsius=
and just that: getters and setters for @temperature
.
But your code does indeed look a bit convoluted. I think this is because Temperature
has to handle both, Fahrenheit and Celsius. You can simplify it by providing separate classes for each temperature scale:
class Celsius
attr_reader :value
def initialize(value)
@value = value
end
def to_celsius
self
end
def to_fahrenheit
Fahrenheit.new((value * 9 / 5.0) + 32)
end
end
class Fahrenheit
attr_reader :value
def initialize(value)
@value = value
end
def to_celsius
Celsius.new((value - 32) * (5 / 9.0))
end
def to_fahrenheit
self
end
end
Now each class has a single instance variable @value
which is being set within initialize
.
temperature = Celsius.new(0)
#=> #<Celsius:0x007fb83d8b33a8 @value=0>
temperature.to_fahrenheit
#=> #<Fahrenheit:0x007fb83d8b3128 @value=32.0>
Upvotes: 0
Reputation: 198314
When you call Temperature.new(c: 0)
, this will set the celsius=
accessor, which sets the instance variable @temperature
(which is meant to always be in Celsius) to 0
.
When you call Temperature.new(f: 32)
, this will set the fahrenheit=
accessor, which sets the instance variable @temperature
to Temperature.ftoc(32)
, or 0.0
.
Calling in_celsius
simply returns @temperature
, or 0
in the example.
Calling in_fahrenheit
returns Temperature.ctof(0)
, or 32.0
.
There is nothing magical about an instance variable being defined outside the constructor. The key point is that it is a variable that is available throughout the instance methods.
Upvotes: 1