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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.