RE: Klassenvariablen nicht erwartungskonform
"Christoph" <[email protected]>
| Newsgroups | gmane.comp.lang.ruby.german |
|---|---|
| Message-ID | <000701c21cdd$e8da5c90$0100a8c0@lony> |
> clemens fischer
...
> wie realisiert man eine variable,
>
> - die in jeder instanz einer klasse getrennt existiert?
---
p self # => main
# also ist
@me = 42
# eine Instanz-Variable des Top-Objekts main
# (eine Instanz der Klasse Object)
# wobei @me == 42
class B
def initialize(x)
# self ist ein zu initialisierendes
# B Object
@x = x
@y = self
end
end
a = Object.new
b = B.new(a)
p b.instance_variables # => ["@y","@x"]
# Wiederauferstehung
aa = b.instance_eval("@x")
bb = b.instance_eval("@y")
p a == aa # => true
p b == bb # => true
class A
p self # => A
# deshalb definiert
@me = self
# eine Instanz-variable @me der Klasse A
# (eine Instanz der Klasse Class)
# wobei @me == self == A
end
# As Singleton-Methoden, mit anderen Worten As
# Klassen-Methoden, sehen As Instanz-variable @me
def A.mee
@me
end
p A.mee # => A
# Kleine extra Verwirrung: Gebrauch einer
# ``Class instance method
class Class
attr_writer :me
end
A.me= A is a Class
p A.mee # => A is a Class
---
> - die in einer klasse fuer alle ihre instanzen global ist?
Solche Variablen gibt es in Ruby nicht, du kannst dir aber
Substituten basteln. Leider ist das nicht ganz einfach - Die
Beispiele lassen leidige Sichtbarkeits-Probleme
(private, protected, public) ausser Acht.
---
# Die beiden ersten Beispiele sollte man wirklich verstehen
class Beispiel1
@@__var__ = Hash.new nil
def klass_var
@@__var__[type]
end
def klass_var=(rhs)
@@__var__[type] = rhs
end
end
class Beispiel2
K__VAR__ = Hash.new nil
def klass_var
K__VAR__[type]
end
def klass_var=(rhs)
K__VAR__[type] = rhs
end
end
class Beispiel3
@__var__ = nil
def klass_var
type.__var__
end
def klass_var=(rhs)
type.__var__= rhs
end
class << self
attr_accessor :__var__
private
def inherited(sub_klass)
sub_klass.instance_eval { @__var__ = nil }
end
end
end
class Beispiel4 # benötigt 1.7
__var__ = nil
define_method(:klass_var){ __var__ }
define_method(:klass_var=) {|rhs| __var__ = rhs }
class << self
private
def inherited(sub_klass)
sub_klass.class_eval <<-klass_mth
__var__ = nil
define_method(:klass_var){ __var__ }
define_method(:klass_var=) {|rhs| __var__ = rhs }
klass_mth
end
end
end
---
Man kann alle Bespiele auch automatisieren (nette Übungs-Aufgabe;-),
persönlich halte ich es aber einfacher. Ich definiere mir halt
eine Klassen-Instanz-Variable und dazugehörige ``Klassen-Attribute,
auf die ich in den Instanz-Methoden via ``type falls nötig
polymorph zugreifen kann. Die ``inherited(sub_klass)
Initialisierung lässt sich sowie nicht über einen Kamm scheren.
Das (nicht Thread-sichere) Beispiel einer Instanz zählenden
Klasse.
---
class Beispiel
@nums = 0
@@nums = 0
def initialize
@num = type.instance_eval { @nums+=1 }
@total_num = @@nums+= 1
end
def number
@num
end
def total_number
@@total_num
end
def clone
super.num_ajust
end
def dup
super.num_ajust
end
protected
def num_ajust
@num = type.instance_eval { @nums+=1 }
@total_num = @@nums+= 1
self
end
class << self
def nums
@nums
end
private
def inherited(sub_klass)
sub_klass.instance_eval {
@nums = 0
}
end
end
end
class A < Beispiel; end
b = Beispiel.new
a = A.new
p a,b,b.clone
# für meine Maschine
#<A:0x100e9ae0 @num=1, @total_num=2>
#<Beispiel:0x100e9b70 @num=1, @total_num=1>
#<Beispiel:0x100e99d8 @num=2, @total_num=3>
---
---
# In der 1.7 lässt sich das ``Marshaling Schlupfloch
# etwas leichter stopfen.
class Bespiel
@nums = 0
@@nums = 0
def number
@num
end
def total_number
@@total_num
end
def clone
super.num_ajust
end
def dup
super.num_ajust
end
protected
def num_ajust
@num = type.nums
@total_num = @@nums
self
end
private
def _dump(depth = -1)
ivars = instance_variables - ["@num","@total_num"]
jvars = ivars.collect { |iv|
instance_eval(iv)
}
Marshal.dump([ivars,jvars])
end
class << self
def nums
@nums
end
private
def allocate
@nums +=1
@@nums += 1
super.instance_eval {
@num = type.nums
@total_num = @@nums
self
}
end
def inherited(sub_klass)
sub_klass.instance_eval {
@nums = 0
}
end
def _load(str)
allocate.instance_eval {
a,b = Marshal.load(str)
a.each_index {|i|
eval "#{a[i]} = b[i]"
}
self
}
end
end
end
class A < Beispiel
def initialize(name)
@name = name
end
end
b = Beispiel.new
a = A.new('Fritz Walter')
p a,Marshal.load(Marshal.dump(a)),b,b.clone
#<A:0x100e9768 @name="Fritz Walter", @total_num=2, @num=1>
#<A:0x100e8df0 @name="Fritz Walter", @total_num=3, @num=2>
#<Beispiel:0x100e9828 @total_num=1, @num=1>
#<Beispiel:0x100e8bf8 @total_num=4, @num=2>
---
> - die fuer jede abgeleitete klasse global ist?
Eine Ruby Klassen-Variable also etwa
---
class Hip
@@top = 42
def top
@@top
end
def self.top
@@top
end
end
class Hop < Hip; end
hip = Hip.new
hop = Hop.new
p hip.top # => 42
p hop.top # => 42
p Hip.top # => 42
p Hop.top # => 42
---
>
> - und was koennte man sich sonst noch wuenschen? ich meine damit auch
die
> sichtbarkeitsregeln und accessoren!
Heute nicht mehr ...
>
> ausserdem wuesste ich gerne, welche bedeutung UML hat. diese sprache
habe
> ich eben erst nach-gegooglet, vorher kannte ich das nicht. und wie
> unterscheiden sich klassen- und instanzvariablen zwischen ruby und
java,
> C++ auf der anderen seite?
Okay, die Klassen-Variablen von C++ können von allen
Klassen-Instanzen eingesehen werden, in dieser Hinsicht
ähneln sie Rubys Klassen-Variablen, mit dem kleinen
aber feinen Unterschied, dass die Ersteren nicht in
Unterklassen sichtbar sind.
Auf der anderen Seite kann man in C++s Klassen-Methoden
(static methods) nur Klassen-Variablen (static variables)
verwendet, was wiederum dem Verhalten von Rubys
Klassen-Instanz-Variablen näher kommt.
/Christoph