next up previous contents index
Search Next: Design Patterns inklusive Up: Fortgeschrittene Konzepte Previous: Dynamisch erstellte Anweisungen   Contents   Index


Basisklassen erweitern und Metaprogrammierung

Durch Erweiterungen der Basisklassen kann man das Grundverhalten von Ruby-Objekten und -Klassen fast beliebig ändern.

Im folgenden Beispiel wird Ruby um das aus Java bekannte "`Schlüsselwort"' synchronized erweitert. Mit ihm kann sichergestellt werden, dass zum Beispiel in einer Buchhaltungsapplikation zwei Methoden einzahlen und auszahlen niemals gleichzeitig ausgeführt werden.

require 'synchronized'  # siehe unten

class Konto
  def initialize(initialBetrag = 0)
    @kontostand = initialBetrag
  end
  def einzahlen(betrag)
    @kontostand += betrag
  end
  def auszahlen(betrag)
    @kontostand -= betrag
  end
  synchronized :einzahlen, :auszahlen
end

Die dafür nötige Verwaltung von Mutex-Objekten erscheint aber nicht in der Applikationslogik, sondern wird über den Aufruf von synchronized von Ruby hinter den Kulissen erledigt.

require 'thread'

class Module
  def synchronized(*methods)
    if methods.size > 0 then
      instance_eval("@@__dclmtx__ = Mutex.new")
      # ein mutex-getter
      module_eval  <<-EOS
        def __mutex__
          if (@__mtx__.nil?) then 
            @@__dclmtx__.synchronize {
              @__mtx__ = Mutex.new if @__mtx__.nil?
            }
          end 
          return @__mtx__
        end
      EOS
    end
    methods.each { | method |
      synchronizeMethod(method.to_s)
    }
  end

private
  def synchronizeMethod(method)
    # Originalmethode 
    instance_eval(
      "alias_method :__#{method}__, :#{method}")
    # Synchronisierter Wrapper
    module_eval <<-EOS 
      def #{method}(*args)  
        __mutex__.synchronize { 
          __#{method}__(*args) 
        } 
      end
    EOS    
  end 
end


\epsfig{height=36pt,file=images/chan.eps}Mit synchronized :einzahlen, :auszahlen wird in der Klasse Konto und allen Subklassen ein klassenweit gültiger "`declaration mutex"' @@__dclmtx__ erzeugt, der sicherstellt, dass in jeder Konto-Instanz nur ein Mutex-Objekt enthalten ist.

In der Methode synchronizeMethod werden die zu synchronisierenden Methoden mit alias umbenannt und Wrapper-Methoden mit den Originalnamen definiert, welche über die kontenbezogenen Mutex-Objekte dafür sorgen, dass keine Kollisionen auftreten. \epsfig{height=10pt,file=images/ruby.eps}



(C) 2002 by dpunkt.de, Armin Roehrl, Stefan Schmiedl, Clemens Wyss 2002-01-20