Loading...
 

Klassen, Methoden und OOP


Hier geht es um Objekte, Klassen, Methoden und Eigenschaften. Kurz gesagt handelt es sich dabei um die OOP - Objekt-Orientierte-Programmierung.

Wenn Ihr bisher nur mit Programmiersprachen zu tun hattet, die nicht objektorientiert sind, hab Ihr am Anfang vielleicht etwas Schwierigkeiten, den Überblick zu behalten. Das liegt nicht zuletzt daran, das man bei OOP nicht mehr von Funktionen sondern von Methoden spricht und Variablen nennt man jetzt plötzlich Eigenschaften. Ich kann mich bis heute nicht mit diesen Begrifflichkeiten anfreunden.

Die Klasse

Unter einer Klasse kann man sich eine kleines Unterprogramm vorstellen. Diese Unterprogramm kann eigene Variablen (Eigenschaften) und Funktionen (Methoden) haben. Üblicherweise werden Klassen in eigenen Dateien ausgelagert, was die Übersicht erheblich verbessert. In unseren kleinen Beispielen steht die Klasse direkt im Hauptprogramm.

Das folgende kleine Script legt eine neue Klasse an.

# Definition einer Klasse mit dem Schlüsselwort 'class'
# Der Klassenname muss mit einem Großbuchstaben beginnen
class Kern
    # Funktionen innerhalb von Klassen nennt man Methoden
    def set_name(der_name : String)
        # Variablennamen die innerhalb der Klasse global gelten
        # werden mit @ geschrieben
        @name = der_name
    end

    # Diese Methode gibt den Inhalt der Variable @name zurück
    def get_name
        @name
    end
end

# Hauptprogramm
# Eine neue Instanz von der Klasse Kern wird angelegt
atom = Kern.new

# die Variable '@name' in der Klasse belegen
# dabei wird die Funktion 'set_name' aufgerufen und der Parameter 'Wasserstoff' übergeben
atom.set_name "Wasserstoff"

p! atom.get_name


Ausgabe:
atom.get_name # => "Wasserstoff"


Beim Instanziieren einer Klasse wird im Speicher eine Kopie der Klasse angelegt. Auf die Kopie kann man dann mit der zugewiessenen Variable 'atom' zugreifen. Der Methodenaufruf (Funktionsaufruf) einer Methode innerhalb einer Klasse wird mit dem '.' Operator veranlasst.

initialize oder der Konstruktor

Als Konstruktor versteht man eine spezielle Methode die direkt beim Anlegen einer Klasse aufgerufen wird. Guten Programmierstil verwendet diese Technik. Bei Crystal wird er Konstruktor als normale Methode mit dem Namen 'initialize' definiert.

class Kern
    # Konstruktor definieren
    def initialize(der_name : String)
        @name = der_name
    end

    def set_name(der_name : String)
        @name = der_name
    end

    def get_name
        @name
    end
end

# Eine neue Instanz von der Klasse Kern wird angelegt
atom = Kern.new "Sauerstoff"
p! atom.get_name

# die Variable '@name' in der Klasse belegen
atom.set_name "Wasserstoff"
p! atom.get_name


Ausgabe:
atom.get_name # => "Sauerstoff"
atom.get_name # => "Wasserstoff"

getters und setters

Im obigen Beispiel haben wird die Methoden 'set_name' und 'get_name' definiert. Solche Methoden nennt man Setter und Getter-Methoden.

Für diesen Zweck gibt es spezielle Makros.

class Kern
    # Setter Methode
    setter name

    # Getter Methode
    getter name

    # Konstruktor definieren
    def initialize(der_name : String)
        @name = der_name
    end

end

# Eine neue Instanz von der Klasse Kern wird angelegt
atom = Kern.new "Sauerstoff"
p! atom.name

atom.name = "Wasserstoff"
p! atom.name


Ausgabe:
atom.name # => "Sauerstoff"
atom.name # => "Wasserstoff"

property

Anstelle von 'getter' _und_ 'setter' kann man auch einfach 'property' verwenden. Das Makro 'property' vereint die beiden anderen.

class Kern
    # property anstelle von getter und setter
    property name

    # Konstruktor definieren
    def initialize(der_name : String)
        @name = der_name
    end
end


# Eine neue Instanz von der Klasse Kern wird angelegt
atom = Kern.new "Sauerstoff"
p! atom.name

atom.name = "Wasserstoff"
p! atom.name


Ausgabe:
atom.name # => "Sauerstoff"
atom.name # => "Wasserstoff"

Variablen Typen

In einer Klasse müssen die Typen von Variablen bestimmt werden. In den bisherigen Beispielen haben wir diese Bestimmung im Methodenaufruf vorgenommen (der_name : String).

Es gibt aber noch weitere Möglichkeiten:

Typbestimmung beim Makro

class Kern
    # Property Methode mit Typbestimmung
    property name : String

    # Konstruktor definieren
    def initialize(der_name)
        @name = der_name
    end
end


Typbestimmung in der Klassenebene

class Kern
  # Typbestimmung in der Klassenebene
  @name : String

  property name

  # Konstruktor definieren
  def initialize(der_name)
  	@name = der_name
  end
end


Typbestimmung im Konstruktor durch Zuweisung eines Wertes

class Kern
    property name
 
    # Konstruktor definieren
    def initialize
        # Typ wird durch die Zuweisung bestimmt
        @name = "Silizium"
    end
end


Typbestimmung in der Methodendefinition

class Kern
    property name
 
   # Variablen Typbestimmung mit "Default"-Wert
   def initialize(der_name = "Schwefel")
       @name = der_name
   end
end

Klassenvariablen


Diese spezielle Art von Variablen gelten nicht wie üblich für jede einzelne Instanz einer Klasse, sonder über alle Instanzen hinweg.

class Kern
	# Klassenvariablen werden hier definiert
	# diese brauchen @@ vorangestellt
	@@id = 0_u32
	
	# Geter und Setter auf id_instance
	property id_instance : UInt32

	# Im Konstruktor könnte man einen Zähler laufen lassen
	def initialize
		@@id += 1
    	@id_instance = @@id
	end

	# das braucht man, um von aussen darauf zugreifen zu können 
	def id
		@@id
	end
end

# Hauptprogramm
wasserstoff = Kern.new
sauerstoff = Kern.new

# Die Klassenvariablen ist bei jeder Instanz gleich
p! wasserstoff.id
p! sauerstoff.id

# Im Vergleich die Insantvariable
p! wasserstoff.id_instance
p! sauerstoff.id_instance


Ausgabe:
wasserstoff.id # => 2
sauerstoff.id # => 2
wasserstoff.id_instance # => 1
sauerstoff.id_instance # => 2