Einleitung: Objektorientierte Programmierung in R

Die objektorientierte Programmierung (OOP) ist ein Konzept, bei dem Daten und zugehörige Funktionen in sogenannten Objekten zusammengefasst werden. Dies fördert die Strukturierung, Wiederverwendbarkeit und Wartbarkeit von Code – besonders bei komplexeren Programmen.

R bietet dafür mehrere Systeme: S3, S4 und das modernere, klassenbasierte R6-System. Anders als S3 und S4 orientiert sich R6 stärker an klassischen OOP-Sprachen wie Python oder Java. Es erlaubt die Definition von Klassen mit Konstruktoren, Methoden, Vererbung und Zugriffskontrolle.

Ziel dieses Beispiels

In diesem Notebook definieren wir eine einfache R6-Klasse namens Zahl, die grundlegende Rechenoperationen kapselt: Addition, Subtraktion, Multiplikation und Division.

Dabei werden zwei häufige Fehlerquellen berücksichtigt:

  • Division durch null
  • Ungültiger Operand (z. B. kein Zahl-Objekt)

Das Beispiel dient als verständlicher Einstieg in OOP mit R6. Es zeigt, wie man Werte und zugehörige Operationen in einer klaren, objektorientierten Struktur verpackt und gleichzeitig robuste Fehlerbehandlung integriert.

Klassendefinition

library(R6)

# Definition der Klasse "Zahl"
Zahl <- R6Class("Zahl",
  public = list(
    wert = NULL,  # Attribut zur Speicherung des Zahlenwerts
    
    # Konstruktor
    initialize = function(wert) {
      self$wert <- wert
    },
    
    # Addition
    addiere = function(anderer) {
      private$check_typ(anderer)
      return(Zahl$new(self$wert + anderer$wert))
    },
    
    # Subtraktion
    subtrahiere = function(anderer) {
      private$check_typ(anderer)
      return(Zahl$new(self$wert - anderer$wert))
    },
    
    # Multiplikation
    multipliziere = function(anderer) {
      private$check_typ(anderer)
      return(Zahl$new(self$wert * anderer$wert))
    },
    
    # Division (mit Fehlerprüfung)
    dividiere = function(anderer) {
      private$check_typ(anderer)
      if (anderer$wert == 0) stop("Fehler: Division durch null ist nicht erlaubt.")
      return(Zahl$new(self$wert / anderer$wert))
    },
    
    # Ausgabe
    anzeigen = function() {
      cat("Wert:", self$wert, "\n")
    }
  ),
  
  private = list(
    # Interne Typprüfung
    check_typ = function(obj) {
      if (!inherits(obj, "Zahl")) {
        stop("Fehler: Argument ist kein Objekt der Klasse 'Zahl'.")
      }
    }
  ) 
)
# Beispielnutzung
a <- Zahl$new(12)
a
<Zahl>
  Public:
    addiere: function (anderer) 
    anzeigen: function () 
    clone: function (deep = FALSE) 
    dividiere: function (anderer) 
    initialize: function (wert) 
    multipliziere: function (anderer) 
    subtrahiere: function (anderer) 
    wert: 12
  Private:
    check_typ: function (obj) 
b <- Zahl$new(4)
b
<Zahl>
  Public:
    addiere: function (anderer) 
    anzeigen: function () 
    clone: function (deep = FALSE) 
    dividiere: function (anderer) 
    initialize: function (wert) 
    multipliziere: function (anderer) 
    subtrahiere: function (anderer) 
    wert: 4
  Private:
    check_typ: function (obj) 
a$addiere(b)$anzeigen()        # 12 + 4 = 16
Wert: 16 
a$subtrahiere(b)$anzeigen()    # 12 - 4 = 8
Wert: 8 
a$multipliziere(b)$anzeigen()  # 12 * 4 = 48
Wert: 48 
a$dividiere(b)$anzeigen()      # 12 / 4 = 3
Wert: 3 
# Fehlerbeispiele (auskommentiert, damit sie den Code nicht abbrechen)
# a$dividiere(Zahl$new(0))     # Division durch null
# a$addiere("nicht_zahl")      # Typfehler
LS0tCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCgojIyBFaW5sZWl0dW5nOiBPYmpla3RvcmllbnRpZXJ0ZSBQcm9ncmFtbWllcnVuZyBpbiBSCgpEaWUgb2JqZWt0b3JpZW50aWVydGUgUHJvZ3JhbW1pZXJ1bmcgKE9PUCkgaXN0IGVpbiBLb256ZXB0LCBiZWkgZGVtIERhdGVuIHVuZCB6dWdlaMO2cmlnZSBGdW5rdGlvbmVuIGluIHNvZ2VuYW5udGVuICpPYmpla3RlbiogenVzYW1tZW5nZWZhc3N0IHdlcmRlbi4gRGllcyBmw7ZyZGVydCBkaWUgU3RydWt0dXJpZXJ1bmcsIFdpZWRlcnZlcndlbmRiYXJrZWl0IHVuZCBXYXJ0YmFya2VpdCB2b24gQ29kZSDigJMgYmVzb25kZXJzIGJlaSBrb21wbGV4ZXJlbiBQcm9ncmFtbWVuLgoKUiBiaWV0ZXQgZGFmw7xyIG1laHJlcmUgU3lzdGVtZTogKipTMyoqLCAqKlM0KiogdW5kIGRhcyBtb2Rlcm5lcmUsIGtsYXNzZW5iYXNpZXJ0ZSAqKlI2LVN5c3RlbSoqLiBBbmRlcnMgYWxzIFMzIHVuZCBTNCBvcmllbnRpZXJ0IHNpY2ggUjYgc3TDpHJrZXIgYW4ga2xhc3Npc2NoZW4gT09QLVNwcmFjaGVuIHdpZSBQeXRob24gb2RlciBKYXZhLiBFcyBlcmxhdWJ0IGRpZSBEZWZpbml0aW9uIHZvbiBLbGFzc2VuIG1pdCBLb25zdHJ1a3RvcmVuLCBNZXRob2RlbiwgVmVyZXJidW5nIHVuZCBadWdyaWZmc2tvbnRyb2xsZS4KCiMjIyBaaWVsIGRpZXNlcyBCZWlzcGllbHMKCkluIGRpZXNlbSBOb3RlYm9vayBkZWZpbmllcmVuIHdpciBlaW5lIGVpbmZhY2hlIFI2LUtsYXNzZSBuYW1lbnMgYFphaGxgLCBkaWUgZ3J1bmRsZWdlbmRlIFJlY2hlbm9wZXJhdGlvbmVuIGthcHNlbHQ6ICoqQWRkaXRpb24qKiwgKipTdWJ0cmFrdGlvbioqLCAqKk11bHRpcGxpa2F0aW9uKiogdW5kICoqRGl2aXNpb24qKi4gCgpEYWJlaSB3ZXJkZW4gendlaSBow6R1ZmlnZSBGZWhsZXJxdWVsbGVuIGJlcsO8Y2tzaWNodGlndDoKCi0gKipEaXZpc2lvbiBkdXJjaCBudWxsKioKLSAqKlVuZ8O8bHRpZ2VyIE9wZXJhbmQgKHou4oCvQi4ga2VpbiBgWmFobGAtT2JqZWt0KSoqCgpEYXMgQmVpc3BpZWwgZGllbnQgYWxzIHZlcnN0w6RuZGxpY2hlciBFaW5zdGllZyBpbiBPT1AgbWl0IFI2LiBFcyB6ZWlndCwgd2llIG1hbiBXZXJ0ZSB1bmQgenVnZWjDtnJpZ2UgT3BlcmF0aW9uZW4gaW4gZWluZXIga2xhcmVuLCBvYmpla3RvcmllbnRpZXJ0ZW4gU3RydWt0dXIgdmVycGFja3QgdW5kIGdsZWljaHplaXRpZyByb2J1c3RlIEZlaGxlcmJlaGFuZGx1bmcgaW50ZWdyaWVydC4KCgojIyBLbGFzc2VuZGVmaW5pdGlvbgoKYGBge3J9CmxpYnJhcnkoUjYpCgojIERlZmluaXRpb24gZGVyIEtsYXNzZSAiWmFobCIKWmFobCA8LSBSNkNsYXNzKCJaYWhsIiwKICBwdWJsaWMgPSBsaXN0KAogICAgd2VydCA9IE5VTEwsICAjIEF0dHJpYnV0IHp1ciBTcGVpY2hlcnVuZyBkZXMgWmFobGVud2VydHMKICAgIAogICAgIyBLb25zdHJ1a3RvcgogICAgaW5pdGlhbGl6ZSA9IGZ1bmN0aW9uKHdlcnQpIHsKICAgICAgc2VsZiR3ZXJ0IDwtIHdlcnQKICAgIH0sCiAgICAKICAgICMgQWRkaXRpb24KICAgIGFkZGllcmUgPSBmdW5jdGlvbihhbmRlcmVyKSB7CiAgICAgIHByaXZhdGUkY2hlY2tfdHlwKGFuZGVyZXIpCiAgICAgIHJldHVybihaYWhsJG5ldyhzZWxmJHdlcnQgKyBhbmRlcmVyJHdlcnQpKQogICAgfSwKICAgIAogICAgIyBTdWJ0cmFrdGlvbgogICAgc3VidHJhaGllcmUgPSBmdW5jdGlvbihhbmRlcmVyKSB7CiAgICAgIHByaXZhdGUkY2hlY2tfdHlwKGFuZGVyZXIpCiAgICAgIHJldHVybihaYWhsJG5ldyhzZWxmJHdlcnQgLSBhbmRlcmVyJHdlcnQpKQogICAgfSwKICAgIAogICAgIyBNdWx0aXBsaWthdGlvbgogICAgbXVsdGlwbGl6aWVyZSA9IGZ1bmN0aW9uKGFuZGVyZXIpIHsKICAgICAgcHJpdmF0ZSRjaGVja190eXAoYW5kZXJlcikKICAgICAgcmV0dXJuKFphaGwkbmV3KHNlbGYkd2VydCAqIGFuZGVyZXIkd2VydCkpCiAgICB9LAogICAgCiAgICAjIERpdmlzaW9uIChtaXQgRmVobGVycHLDvGZ1bmcpCiAgICBkaXZpZGllcmUgPSBmdW5jdGlvbihhbmRlcmVyKSB7CiAgICAgIHByaXZhdGUkY2hlY2tfdHlwKGFuZGVyZXIpCiAgICAgIGlmIChhbmRlcmVyJHdlcnQgPT0gMCkgc3RvcCgiRmVobGVyOiBEaXZpc2lvbiBkdXJjaCBudWxsIGlzdCBuaWNodCBlcmxhdWJ0LiIpCiAgICAgIHJldHVybihaYWhsJG5ldyhzZWxmJHdlcnQgLyBhbmRlcmVyJHdlcnQpKQogICAgfSwKICAgIAogICAgIyBBdXNnYWJlCiAgICBhbnplaWdlbiA9IGZ1bmN0aW9uKCkgewogICAgICBjYXQoIldlcnQ6Iiwgc2VsZiR3ZXJ0LCAiXG4iKQogICAgfQogICksCiAgCiAgcHJpdmF0ZSA9IGxpc3QoCiAgICAjIEludGVybmUgVHlwcHLDvGZ1bmcKICAgIGNoZWNrX3R5cCA9IGZ1bmN0aW9uKG9iaikgewogICAgICBpZiAoIWluaGVyaXRzKG9iaiwgIlphaGwiKSkgewogICAgICAgIHN0b3AoIkZlaGxlcjogQXJndW1lbnQgaXN0IGtlaW4gT2JqZWt0IGRlciBLbGFzc2UgJ1phaGwnLiIpCiAgICAgIH0KICAgIH0KICApIAopCmBgYAoKCmBgYHtyfQojIEJlaXNwaWVsbnV0enVuZwphIDwtIFphaGwkbmV3KDEyKQphCmIgPC0gWmFobCRuZXcoNCkKYgpgYGAKCmBgYHtyfQphJGFkZGllcmUoYikkYW56ZWlnZW4oKSAgICAgICAgIyAxMiArIDQgPSAxNgphJHN1YnRyYWhpZXJlKGIpJGFuemVpZ2VuKCkgICAgIyAxMiAtIDQgPSA4CmEkbXVsdGlwbGl6aWVyZShiKSRhbnplaWdlbigpICAjIDEyICogNCA9IDQ4CmEkZGl2aWRpZXJlKGIpJGFuemVpZ2VuKCkgICAgICAjIDEyIC8gNCA9IDMKCmBgYAoKYGBge3J9CiMgRmVobGVyYmVpc3BpZWxlIChhdXNrb21tZW50aWVydCwgZGFtaXQgc2llIGRlbiBDb2RlIG5pY2h0IGFiYnJlY2hlbikKIyBhJGRpdmlkaWVyZShaYWhsJG5ldygwKSkgICAgICMgRGl2aXNpb24gZHVyY2ggbnVsbAojIGEkYWRkaWVyZSgibmljaHRfemFobCIpICAgICAgIyBUeXBmZWhsZXIKCmBgYAoK