Explain please, I can not understand.
class Foo
#a = 123
##b = 123
end
What are the advantages of variable objects-classes and the class variables? When should I use first, and in which the second?
Instance level variables area created anew for each instance of the class. For example, a variable #id should probably be unique for each instance of Foo. However, there may be some values that should be the same for every instance of the type. In that case, a class variable would be more appropriate.
One important side effect of class level variables is that they are shared amongst derived classes as well. This means that changing the value in a subclass of 'Foo' will change it for 'Foo' objects as well. This may be what you want, but it can be a bit surprising to find out the hard way.
For example:
class Foo
##some_var = 1
def bar
puts(##some_var)
end
end
class Baz < Foo
def perhaps_unexpected
##some_var = 10
Foo.new.bar #prints '10'
end
end
Use a class variable when you want all the instances of the class to share that variable, and use an instance variable when you want each instance to have its own non-shared variable.
Related
What is the difference between class variable and variable defined on the class level?
say, bar is defined with ## which means it's a class variable and will be accessible within all methods in class.
class Foo
##bar = 'bar'
end
so does bar without ##, so what is the difference..?
class Foo
bar = 'bar'
end
Well, with your second option, bar is a local variable which gets out of scope when reaching the end. As such, it won't be accessible to any methods (class methods nor instance methods) of the class.
With that being said, in Ruby, there are class variables (##bar which are shared between all child classes and their instances and instance variables (#bar) Since classes are also just objects in Ruby, you can also define an instance variable on class level (or more correctly: on the singleton class of your class). This can work like this:
class Foo
def self.bar
#bar
end
def self.bar=(value)
#bar = value
end
end
Compared to class variables, these instance variables on the singleton class are not accessible on Foo instances nor on child classes of Foo.
The ##bar will return the same variable to all instances of the class, not just the same variable to all methods in the class.
The way I think of class variables (##variables) is as namespaced global variables. Their usage is just about as frowned upon as using global variables, but not quite since you are limiting the scope to within the code defined in your class.
Generally they would be used to track some kind of state across your application.
Let's say you had an object that needed to be able to identify its most recently instantiated sibling object.
One way to do that would be via a global variable:
class MyThing
def initialize
$latest_thing = self
end
def latest
$latest_thing
end
end
thing1 = MyThing.new
thing1.latest # => thing1
thing2 = MyThing.new
thing1.latest # => thing2
thing2.latest # => thing2
Now, that uses a global variable which is generally perceived as bad practice, one of the reasons being for pollution of the global namespace and risk of naming collision and/or someone else changing it.
If you are dealing with a situation like this where you need this shared state between instances, but no one outside needs to know about it, then you can use the class variable exactly like a global:
class MyThing
def initialize
##latest_thing = self
end
def latest
##latest_thing
end
end
thing1 = MyThing.new
thing1.latest # => thing1
thing2 = MyThing.new
thing1.latest # => thing2
thing2.latest # => thing2
That's just generally cleaner/safer/better encapsulated, since any 3rd party code can not simply do ##latest_thing = :something_else the way that they could do had a global been used.
Hope that helps. I think class variables are rarely used or encouraged in the wild, but on the rare occasion I've needed to use one it was for things like this.
Here, I create a local variable in class scope:
class MyClass
x = 1
puts x
end
It prints 1 even if I don't create any instances of MyClass.
I want to use x in some method:
class MyClass
x = 1
def method
puts x
end
end
m = MyClass.new
m.method
And I can't. Why? I get that class definition creates a scope, but why is it not accessible in the method? Isn't scope of the method inside the scope of the class?
I can imagine that this is related to creation of a class. Since any class is an object of Class, maybe the scope of MyClass is the scope of some Class method, and the way of coupling methods of MyClass to that instance makes their scope completely different.
It also seems to me that I can't just create a scope with {} (like in C) or something like do..end. Am I correct?
Scope of a method is not inside the class. Each method has its own entirely new scope.
New scopes are created whenever you use the class, module, and def keywords. Using brackets, as in C, does not create a new scope, and in fact you cannot arbitrarily group lines of code using brackets. The brackets (or do...end) around a Ruby block create a block-level scope, where variables previously created in the surrounding scope are available, but variables created within the block scope do not escape into the surrounding scope afterward.
Instance methods share the scope of their instance variables with other instances methods. An instance variable defined in the scope of a class definition is available in class-level singleton methods, but not in instance methods of the class.
Illustration:
class Foo
x = 1 # available only here
#y = 2 # class-wide value
def self.class_x
#x # never set; nil value
end
def self.class_y
#y # class-wide value
end
def initialize(z)
x = 3 # available only here
#z = z # value for this instance only
end
def instance_x
#x # never set; nil
end
def instance_y
#y # never set; nil
end
def instance_z
#z # value for this instance only
end
end
Foo.class_x # => nil
Foo.class_y # => 2
Foo.new(0).instance_x # => nil
Foo.new(0).instance_y # => nil
foo3 = Foo.new(3)
foo4 = Foo.new(4)
foo3.instance_z # => 3
foo4.instance_z # => 4
You can access class-level instance variables from within instances using the class-level getter. Continuing the example above:
class Foo
def get_class_y
self.class.class_y
end
end
foo = Foo.new(0)
foo.get_class_y # => 2
There exists in Ruby the notion of a "class variable," which uses the ## sigil. In practice, there is almost never a reasonable use case for this language construct. Typically the goal can be better achieved using a class-level instance variable, as shown here.
Here, I create a local variable in class scope:
class MyClass
x = 1
puts x
end
It prints 1 even if I don't create any instances of MyClass.
Correct. The class definition body is executed when it is read. It's just code like any other code, there is nothing special about class definition bodies.
Ask yourself: how would methods like attr_reader/attr_writer/attr_accessor, alias_method, public/protected/private work otherwise? Heck, how would def work otherwise if it didn't get executed when the class is defined? (After all, def is just an expression like any other expression!)
That's why you can do stuff like this:
class FileReader
if operating_system == :windows
def blah; end
else
def blubb; end
end
end
I want to use x in some method:
class MyClass
x = 1
def method
puts x
end
end
m = MyClass.new
m.method
And I can't. Why? I get that class definition creates a scope, but why is it not accessible in the method? Isn't scope of the method inside the scope of the class?
No, it is not. There are 4 scopes in Ruby: script scope, module/class definition scope, method definition scope, and block/lambda scope. Only blocks/lambdas nest, all the others create new scopes.
I can imagine that this is related to creation of a class. Since any class is an object of Class, maybe the scope of MyClass is the scope of some Class method, and the way of coupling methods of MyClass to that instance makes their scope completely different.
Honestly, I don't fully understand what you are saying, but no, class definition scope is not method definition scope, class definition scope is class definition scope, and method definition scope is method definition scope.
It also seems to me that I can't just create a scope with {} (like in C) or something like do..end. Am I correct?
Like I said above: there are 4 scopes in Ruby. There is nothing like block scope in C. (The Ruby concept of "block" is something completely different than the C concept of "block.") The closest thing you can get is a JavaScript-inspired immediately-invoked lambda-literal, something like this:
foo = 1
-> {
bar = 2
foo + bar
}.()
# => 3
bar
# NameError
In general, that is not necessary in Ruby. In well-factored code, methods will be so small, that keeping track of local variables and their scopes and lifetimes is really not a big deal.
So just creating a class without any instances will lead to something
actually executing in runtime (even allocating may be)? That is very
not like C++. –
Check out this code:
Dog = Class.new do
attr_accessor :name
def initialize(name)
#name = name
end
end
If you execute that code, there won't be any output, but something still happened. For instance, a global variable named Dog was created, and it has a value. Here's the proof:
Dog = Class.new do
attr_accessor :name
def initialize(name)
#name = name
end
end
dog = Dog.new("Ralph")
puts dog.name
--output:--
Ralph
The assignment to the Dog constant above is equivalent to writing:
class Dog
...
...
end
And, in fact, ruby steps through each line inside the class definition and executes each line--unless the line of code is inside a def. The def is created but the code inside a def doesn't execute until the def is called.
A very common line you will see inside a class definition is:
attr_accessor :name
...which can be rewritten as:
attr_accessor(:name)
...which makes it obvious that it's a method call. Ruby executes that line--and calls the method--when you run a file containing the class definition. The attr_accessor method then dynamically creates and inserts a getter and a setter method into the class. At runtime. Yeah, this ain't C++ land anymore--welcome to NeverNever Land.
I get that class definition creates a scope, but why is it not
accessible in the method?
Because that is the way Matz decided things should be: a def creates a new scope, blocking visibility of variables outside the def. However, there are ways to open up the scope gates, so to speak: blocks can see the variables defined in the surrounding scope. Check out define_method():
class MyClass
x = 1
define_method(:do_stuff) do
puts x
end
end
m = MyClass.new
m.do_stuff
--output:--
1
The block is everything between do...end. In ruby, a block is a closure, which means that when a block is created, it captures the variables in the surrounding scope, and carries those variables with it until the the block is executed. A block is like an anonymous function, which gets passed to a method as an argument.
Note that if you use the Class.new trick, you can open two scope gates:
x = 1
MyClass = Class.new do
define_method(:do_stuff) do
puts x
end
end
m = MyClass.new
m.do_stuff
--output:--
1
Generally, ruby allows a programmer to do whatever they want, rules be damned.
I can create a class using a constant name with class keyword:
class MyClass1; end
I can also create a class with Class.new and assign that to a constant or a variable:
MyClass2 = Class.new do; end
myClass3 = Class.new do; end
but I cannot create a class using class keyword with a name that begins in lowercase:
class myclass4; end # => Error
Is there a fundamental difference between these four? Isn't myclass3 a regular class?
The first method (class MyClass; end) is an explicit part of the language syntax (in that class is a keyword), and the class’s name must be a constant.
The second method (Class.new) is just a normal method call, and returns an anonymous instance of Class. This anonymous instance can then be treated like any other object.
Other than that, there are no differences between the two methods. Note that you can still assign the first type into a non-constant, but it must also first be assigned into a constant:
class MyClass; end
my_class = MyClass
Similar to Andrew Marshall's answer, but also different.
Think of it this way:
The class Foo ... syntax does more than defining a class; in addition to defining a class, it necessarily names it when called for the first time, and a class name must be a constant. Note that, in principle, a class can be nameless (see #3).
On the other hand, assignment can be done to a variable or a constant. There is no restriction to that.
A purer way to create a class is to use Class.new. Using this syntax, you do not have to name it. So it is okay to assign it to a variable:
foo = Class.new
# => #<Class:0x007f36b23159a8>
Only when it is assigned to a constant for the first time does it get named:
Foo = foo
# => Foo
I've come across the following example from this tutorial:
class Song
##plays = 0
def initialize(name, artist, duration)
#name = name
#artist = artist
#duration = duration
#plays = 0
end
def play
#plays += 1
##plays += 1
"This song: ##plays plays. Total ###plays plays."
end
end
s1 = Song.new("Song1", "Artist1", 234) # test songs
s2 = Song.new("Song2", "Artist2", 345)
puts s1.play
puts s2.play
puts s1.play
puts s1.play
Is ##plays politely accessible only inside the class Song? This commentary brings up the point of not recommending the use of class variables. Is it b/c they are often not required in common-day use, and create a lot of debugging headaches when used?
Class variables are never really required. But the reason isn't that they're shared state. I mean, it's good to avoid shared state where you can, but that's not the real problem here.
The reason they're recommended against is, as shown in that article, they are really confusing. In particular, a class's class variables are shared by its subclasses and instances of its subclasses. For example:
class Parent
end
class Child1 < Parent
##class_var = "Child1's"
end
class Child2 < Parent
##class_var = "Child2's"
end
With this code, Child1 and its instances will all see a class variable named ##class_var with the value "Child1's" and Child2 and its instances will all see a class variable named ##class_var with the value "Child2's". But suppose later on we reopen Parent and write this:
class Parent
##class_var = "Parent's"
end
Now Parent and the instances it creates will all see a class variable named ##class_var with the value "Parent's". But that's not all. Now that the parent class has this variable, Child1 and Child2 suddenly share the variable, so all of the ##class_vars have the value "Parent's". And if you reassign the variable in Child1, it's still shared, so all of the classes get updated. How confusing!
Instead of class variables, you can just use instance variables of the class, like this:
class Parent
#class_var = "Parent's"
def self.class_var
#class_var
end
end
class Child1 < Parent
#class_var = "Child1's"
end
class Child2 < Parent
#class_var = "Child2's"
end
Now, Parent.class_var will return "Parent's", Child1.class_var will return "Child1's" and Child2.class_var will return "Child2's" — just like you expect.
A class variable is a variable that is shared among all instances of a class. This means only one variable value exists for all objects instantiated from this class. This means that if one object instance changes the value of the variable, that new value will essentially change for all other object instances. Another way of thinking of thinking of class variables is as global variables within the context of a single class.
##plays #is a class variable
#plays #is an instance variable
$plays #is a global variable accessed outside a class
So in your example you created a class variable ##plays to calculate the total number of songs played for all songs. Since it is a class variable, it cannot be accessed outside the class alone. If you wanted to access the total number of plays you can use a global variable. They start with a dollar sign $plays (in your case). I warn you, you should stay away from using global variables as they are problematic for numerous reasons. One thing you may consider is to create a method that pushes all song instances into an array. You can then sum all plays across all songs through iterators. Way more secure, way less prone to programmer error.
Edit:
Here are why global variables are bad
Are global variables bad?
The ## variable will a class variable. This is generally bad practice. In your code its redundant because #plays == ##plays (unless you set ##plays elsewhere in your code (bad practice))
Actually now that I look at it, they aren't really the same. #plays keeps a count of how many times an individual song has been played, and ##plays will keep a count of all songs. Still, its likely bad practice to use ##plays. Usually, you'd have a parent class like "Player" that is managing all the songs. There should be an instance variable called #total_plays in the "Player" class.
Sometimes I see an instance variable defined as #my_variable. However, sometimes I see self.my_variable. When is each used?
Instance variables (#variable) correspond to private variables in other languages. self.myvariable is actually not a variable, but a call to a method. Similarly, if you write self.myvariable = something, it is actually a call to self.myvariable=(something). This corresponds to properties with getters and setters in other languages.
class Foo
def initialize
#bar = 42
end
def xyzzy
123
end
def xyzzy=(value)
puts "xyzzy set to #{value}!"
end
end
obj = Foo.new
puts obj.xyzzy # prints: 123
obj.xyzzy = 2 # prints: xyzzy set to 2
puts obj.bar # error: undefined method 'bar'
You can use attr_reader and attr_accessor to automatically define getters and setters for an instance variable. attr_reader will only generate a getter, while attr_accessor generates both.
class Parrot
attr_accessor :volts
def voom
puts "vooming at #{#volts} volts!"
end
end
polly = Parrot.new
polly.volts = 4000
polly.voom
Instance variables are more primary things than methods calling them. In self.myVariable, myVariable is a method referring to the instance variable #myVariable, and that method is defined usually by attr_reader or attr_accessor.
One purpose of object orientated programming is to encapsule things particular to an instance inside that instance and make it inaccessible from outside of it. This way, you can avoid unwanted conflicts of name. This is true for instance variables. They are usually parameters to be handeled within the instance, and not to be used outside of it.
Within an instance, its instance variables can be directly referred to, and hence there is no need to refer to them via method calls. You should directly call the variable #myVariable.
From outside of an instance, you cannot directly refer to the instance variables because of the reason mentioned above. But sometimes, you do need to refer to them. The purpose of using the method myVariable is to refer to the instance variable from outside of an instance.
#my_variable refers directly to the instance variable, and is (for the most part) inaccessible from outside that instance.
self.my_variable is using an accessor method (as defined with attr_reader, attr_writer or attr_accessor internally. This is in cases where there may not be an instance variable named #my_variable (as is the case with ActiveRecord model attributes) or where the internal state differs from what is exposed publicly.