Get- / Set Methoden

Hallo,
im Informatikunterricht lernt man, dass man nie auf die Attribute direkt zugreifen sollte und statt dessen lieber für jedes Attribut eine "get_attribut_a" und eine "set_attribut_a" Methode schreibt. Ich habe nie wirklich verstanden warum man so etwas tun sollte, da der direkte Zugriff viel weniger Code braucht und auch beim Zugriff ersichtlicher ist. Stimmt es das es üblich ist get und set Methoden in Klassen einzubauen? Falls ja, warum ist das so?

Falls ihr nicht wisst was ich meine:
direkter Zugriff(Code in der Klasse erübrigt sich):
Code:
x.input_protocol = "imap"
print x.input_protocol

get und set-Methoden in der Klasse:
Code:
def get_input_protocol(self):
        return input_protocol
def set_input_protocol(self, p_input_protocol):
        input_protocol = p_input_protocol

Zugriff mit get und set:
Code:
x.set_input_protocol("imap")
print x.get_input_protocol

Warum wurde mir erzählt das es üblich ist die 2. Methode zu verwenden?

MfG Stein
 
Eine Antwort ist wohl daß ja eine getter- bzw. setter-Methode ja auch aus mehr als nur dem setzen und zurückgeben der Variable bestehen kann. Beispielsweise könnte man im Setter den übergebenen Wert inhaltlich prüfen bzw. sogar ändern. Im Getter könnte analog dazu der Variableninhalt in irgendeinerweise formatiert zurückgegeben werden. Würde man in einem Programm mal den Getter und mal den direkten Zugriff auf die Variable benutzen kann das zu unschönen Ergebnissen führen. Ergo ist es besser strikt immer die Getter und Setter zu benutzen.
 
Abgesehen davon ist der Performancenachteil bei get-/set-Methoden auf einem PC nicht spürbar.

Natürlich gibt es auch Gebiete, bei denen man möglichst auf diese Methoden verzichten sollte, nämlich wenn man beispielsweise einen Microcontroller programmiert.
Gemessen haben wir das mal an der Ausführungszeit von identischen (Java- bzw. Lejos-)Programmen auf Lego Mindstorms, einmal mit get-/set-Methoden und einmal per direktem Zugriff.
 
Zum einen kann man mit der OO auch übertreiben (der "getter/setter Wahn" ;)) - zum anderen ist Python nun nicht wirklich zum nachvollziehen dieser Philosophie geeignet, da es doch sich eher an ganz andere Entwicklungsmuster orientiert (sonst würde man die "dynamik" nicht Ansatzweise ausnutzen) ;). In Python gibt es schließlich keine wirklich privaten Attribute und man kann "mal eben" einer Klasse oder gar Instanz einfach mal ein zusätzliches Attribut (oder Methode) verpassen (was auch ganz nett und nutzlich sein kann - auch wenn man es als "dreckig" empfindet)

Andererseits - ein praktisches Beispiel, warum Getter/Setter auch in Python nützlich sein können:

Der Code bildet eine Art Abhängigkeit in einem Ablauf als Baum ab und versucht diese dann möglichst weit zu vereinfachen, bevor damit weiterer Unfug angestellt wird.
Vorläufige Implementierung:
Code:
class Node(object):
    ....
class FooNode(Node):
    ....
    def reduce(self):
      ....

class BarNode(Node):
    def reduce(self):
        left = self.childs[0].reduce()
        right = self.childs[1].reduce()
        ...
        if left == foo:
              return BarNode(left)
        return FooNode(left, right)
Es werden also die Kinder reduziert/vereinfacht und dann aus diesem (je nach Ergebnis) ein neuer Node erzeugt.
Funktioniert erstmal wunderbar - bis man die "reduce" Funktion soweit verfeinert hat, dass sie auch ein "direktes" True/False zurückgibt und man nette AttributeError Exceptions um die Ohren geworfen bekommt ;).
Was nun? Jeden Zugriff auf die "childs" mit Try/Except umwrappen == viel zu viel Schreibarbeit.
Aber ein Getter-Setter kann dies einem durchaus ersparen:
Code:
class Node(object):
    def get_child(self, n):
        if isinstance(self.childs[n], Node):
            return self.childs[n]
        else:
            return WrapperNode(self.childs[n])

class WrapperNode(Node):
    def reduce(self):
        return self
jetzt ersetzt man einfach alle "self.childs[0]" durch
"self.get_child(0)" und hat sich viele Zeilen Code erspart und diesen sogar flexibler gemacht ;).
(ok, als Schlangenguru kann man auch __getattr__ der Klasse überschreiben - aber ist zum einen genausoviel Tipparbeit, zum anderen etwas komplizierter in der Handhabung ;) ).

Und bei größeren Projekten versucht man mit Getter/Setter, abgestufter Sichtbarkeit der Methoden/Attribute die Abhängigkeiten zu reduzieren und möglichst klare Schnittstellen zu schaffen. Ein direktes Beschreiben eines Attributes kann dann ganz böse enden, wenn z.B irgendwelche intern en Änderungen daran stattfinden. Stichwort wäre auch "Datenkapselung".
 
Ich denke getter und setter unterstützen die Philosophie der OOP. Eine Klasse stellt soetwas wie eine Blackbox da. Es ist nicht interesant wie eine Klasse etwas macht sondern es kommt nur darauf an, dass sie es macht. Methoden stellen somit die Schnittstelle der Klasse zur Außenwelt dar. Wenn ich auf Attribute einer Klasse nur über Methoden zugreifen kann, dann ist das einfach eine sauberere Programmierung, als wenn ich wild von außen alle möglichen Attribute direkt ändern kann.
 
Nunja, der wesentliche Nachteil an gettern/settern ist imho die schlechte Lesbarkeit:
"desktop.getMouse().getCoords().getX ()" deutlich umständlicher zu lesen, als desktop.mouse.coords.x ;)
oder:
mouse.coords.x = 10
vs. "desktop.getMouse().getCoords().setX (10)"

Insofern scheinen mir "Properties" ein ganz guter Kompromiss zu sein:
Properties (C#)
Code:
{
    [COLOR=Blue]private[/COLOR] [COLOR=Blue]double[/COLOR] seconds;

    [COLOR=Blue]public[/COLOR] [COLOR=Blue]double[/COLOR] Hours
    {
        [COLOR=Blue]get[/COLOR] { [COLOR=Blue]return[/COLOR] seconds / 3600; }
        [COLOR=Blue]set[/COLOR] { seconds = value * 3600; }
    }
}

[COLOR=Blue]class[/COLOR] Program
{
    [COLOR=Blue]static[/COLOR] [COLOR=Blue]void[/COLOR] Main()
    {
        TimePeriod t = [COLOR=Blue]new[/COLOR] TimePeriod();

        [COLOR=Green]// Assigning the Hours property causes the 'set' accessor to be called.[/COLOR]
        t.Hours = 24;

        [COLOR=Green]// Evaluating the Hours property causes the 'get' accessor to be called.[/COLOR]
        System.Console.WriteLine([COLOR=#a31515]"Time in hours: "[/COLOR] + t.Hours);
    }
Man hat also in der Klasse selbst getter/setter, "nach außen" hin aber angenehmere/lesbare Syntax.
(die gibt es im übrigen auch für Python - entweder zum selberbasteln mit "__gettattr__" und "__setattr__" oder als "property" Decorator/Built-in)

Edit: wie gesagt, mit OOP kann man auch übertreiben - ich behaupte einfach mal ganz frech, dass hier niemand in der Praxis 100% OOP programmiert bzw. auch es nicht wirklich möchte ;)
 
Noch einen kleinen Tipp: Falls man in die Situation kommt einen Haufen Getter und Setter in einem einzigen Objekt einzubauen sollte man die Struktur überdenken. Objektorientierung ist ja wirklich schön und gut, nur sollte man sie dort einsetzen, wo sie angebracht ist. Kurz gefasst: Probleme innerhalb eines Moduls abgekapselt lösen. Wenn man aber eben viele Getter und Setter benötigt ist entweder das Modul nicht wirklich abgekapselt vom Rest oder das Objekt ist eigentlich nur eine simple Datenstruktur(-ansammlung), welche in ein objektorientiertes Korsett gezwungen wird. Wobei Datenstruktur natürlich nicht böse sind, braucht man auch. Man sollte nur wissen wo man was hat bzw. einsetzt.

mfg benediktibk
 
Natürlich sind Getter und Setter grundsätzlich was Gutes. Oft bildet eine Klasse aber auch 1:1 eine SQL-Tabelle ab wo jedes Member eine Column ist. Da kann man meiner Meinung nach durchaus alle Member public machen. Aber so einen Code lässt man sich meist wohl generieren.
 
wie gesagt, mit OOP kann man auch übertreiben - ich behaupte einfach mal ganz frech, dass hier niemand in der Praxis 100% OOP programmiert bzw. auch es nicht wirklich möchte ;)

ganz ehrlich: ich finde nichts schlimmer, als diese Misch-Formen...
entweder man verwendet grundsätzlich Getter und Setter oder man lässt es bleiben. Aber eine Member-Variable per getter/setter und die nächste direkt - das finde ich absolut abartig...

Da lieber schreibe ich mir ein kleines Auto-Code-Generator-Script, welches mir die Getter und Setter automatisch schreibt und ich nur noch bei den entsprechenden Settern die Eingaben nachträglich per Hand validieren muss, als dass ich direkten Zugriff auf meine Attribute zulasse...

Wir sind @work gerade dabei, das Shop-System auf eine komplett eigene Lösung umzubauen, welche nur noch auf die Datenbank-Struktur des xtCommerce zugreift...
Da gehen wir noch 'nen Schritt weiter und machen nicht nur getter/setter zur Pflicht sondern haben uns - damit es einfacher wird, den Code von Kollegen zu lesen - sogar ne ganz detaillierte Coding-Guideline erarbeitet: Namenskonvention für Funktionen/Klassen/Methoden/Attibute, selbst Klammersetzung und Einrückung sind definiert und die Tatsache, dass das schließende PHP-Tag verboten ist und wie detailiert die DocBlocks zu schreiben sind, etc.

Das klingt für den Anfang vielleicht etwas over-the-top, aber wenn du im Team arbeitest und Code von Kollegen gegenliest, ist es echt wesentlich angenehmer, wenn es einheitliche Richtlinien gibt.
 
ganz ehrlich: ich finde nichts schlimmer, als diese Misch-Formen...
entweder man verwendet grundsätzlich Getter und Setter oder man lässt es bleiben. Aber eine Member-Variable per getter/setter und die nächste direkt - das finde ich absolut abartig...
Die Anmerkung war eine bewusste Provokation (und ich meinte mit OOP nicht nur getter/setter Nutzung) :wink:
Denn striktes OOP bedeutet eigentlich:
alles
ist ein Objekt bzw. wird wie eins behandelt.
Konsequenterweise müsste man dann auch Zahlen als Objekte behandeln -> alle Operationen darauf nur mit Methoden/Nachrichten durchführen. Also statt 1+2*3 sowas schreiben: 1.add(2.mul(4)) oder mit "Syntaxsuggar": 1+(2*(3)) (Die Klammern gehören zum Aufruf der Methode und natürlich gibt es dann keinen Punkt-Vor-Strich).
Das ist auf Dauer genauso umständlich, wie stricktes "alles ist ein Ausdruck"/"alles ist eine Funktion"/"alles ist ein rel. Algebraausdruck aka SQLQuery" ;).
Um ein reales Beispiel zu bringen: ein serieller Ablauf/Protokoll, der in die Objektform "gequetsch" behandelt wird, oder Event-Handling "auf OOP Basis" (das Objekt stellt dann quasi eine große State-Maschine dar :rolleyes: ) ist nicht minder grausam zu lesen und zu warten. Insofern wäre ich eher benedikts Meinung - Einsetzen, wo es angebracht ist und nicht als Allheilmittel ansehen :)
 
Ein weiterer Vorteil von Settern und Gettern ist es, dass man den Zugriff auf Attribute einschränken oder durch Proxies ersetzen kann.

Wenn du direkten Variablenzugriff hast, kannst du diesen durch Subklassen nicht verändern.
Mit Settern und Gettern trennst du eine Klasse in zwei Komponenten: Eine Schnittstellen (Funktions-Deklarationen) und eine Logikkomponente (Member-Variablen und Implementierung). Du kannst dann jederzeit durch Ableitung die Logik verändern, ohne die Schnittstelle modifizieren zu müssen.

Zwei Beispiele:
Code:
class Bar {

  private int foo;
  
  public Bar(int foo) {
    this.foo = foo;
  }

  public int getFoo() {
    return this.foo;
  }

  public void setFoo(int foo) {
    this.foo = foo;
  }

}

class UnmodifiableBar extends Bar {

  public UnmodifiableBar(int foo) {
    super(foo);
  }

  @Overrides
  public void setFoo(int foo) {
    throw new UnsupportedOperationException("Unmodifiable, bitches!");
  }

}

Code:
class Person {

  private String name;
  private int age;
  
  public Person(String name, int age) {
    this.name = name;
    this.age = age;
  }

  public String getName() {
    return this.name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public int getAge() {
    return this.age;
  }

  public void setAge(int age) {
    this.age = age;
  }

}

class PersonFromDatabase extends Person {

  private int id;

  public PersonFromDatabase(int dbId) {
    super(null, null);
    this.id = dbId;
  }

  public String getName() {
    String currentName = getNameFromDatabase(this.id);
    return currentName;
  }

  public void setName(String name) {
    setNameToDatabase(this.id, name);
  }

  public int getAge() {
    int currentAge = getAgeFromDatabase(this.id);
    return currentAge;
  }

  public void setAge(int age) {
    setAgeToDatabase(this.id, age);
  }

}

Etwas klarer geworden, warum indirekter Zugriff auf Attribute in OOP meistens eine gute Idee ist?

mfg, metax.
 
Man sollte ggf. bei der OOP auch die verteilte Entwicklung nicht ganz ausser Acht lassen. Da geht es ja u.a. darum, dass ein Entwickler A nicht wissen muss was Entwickler B in seinen Klassen definiert und tut. Wenn Entwickler A in einer Klasse von Entwickler B einen Wert setzt, dann tut er dies über eine Setter-Methode und wenn er einen Wert liest, tut er dies über eine Getter-Methode. Ihn muss dabei nicht kümmern welche Variablen innerhalb der Klasse von Entwickler B angesprochen werden, wie diese heissen oder ob da noch irgendwelche Prüfungen durchgeführt werden. Er muss nur wissen, dass beim Aufruf von Funktion setXY() ein Wert dem ganzen Objekt bekanntgegeben wird und dass er den Wert XY, der im ganzen Objekt bekannt ist, durch Aufruf von Funktion getXY() erfahren kann. Der Entwickler der Klasse hat somit die Freiheit beliebig die Benennungen seiner Variablen zu ändern, während das "Interface" der Klasse gleich bleiben kann. Ausserdem muss er nicht darauf vertrauen, dass Entwickler A schon die richtigen Daten liefern wird, sondern kann dies nochmal explizit prüfen.

Allgemein denke ich aber auch, dass man manchmal mit OOP auch gern übertreibt. Sinn macht es imo eigentlich nur, wenn viele Leute an einer Software arbeiten, so dass es kaum möglich ist, dass Entwickler A weiss was Entwickler B tut. Die Entwickler müssen dann nur wissen: Es gibt diese und jene Klassen, die diese und jene Daten verarbeiten können und diese und jene Werte bereitstellen.
 
Man kann getter/setter natürlich auch zweckempfremden um zeit zu sparen. ein kürzlich von mir angewendetes beispiel:

Konsolenenanwendung. es geht um einen server.
dieser soll statusmeldungen nach dem Schema "<Datum/Uhrzeit> <Nachricht>" ausgeben. das selbe soll gleichzeitig in die datei <Appdata-Verzichnis>\server.log geschrieben werden (dieser Pfad liegt in der Variablen logfile

das ließe sich so regeln:
VB.NET-Code
Code:
console.WriteLine(now & "Meldung 1")
My.Computer.Filesytem.WriteAllText(logfile,now & "Meldung 1" & vbNewLine,True)
console.WriteLine(now & "Meldung 1")
My.Computer.Filesytem.WriteAllText(logfile,now & "Meldung 1" & vbNewLine,True)
console.WriteLine(now & "Meldung 1")
My.Computer.Filesytem.WriteAllText(logfile,now & "Meldung 1" & vbNewLine,True)
'...

Gut man könnte es auch als methode schreiben, aber dann vergess zumindest ich gerne die klammern ;) Aber mun die Version mit Propertys also getter/setter

VB.NET-Code
Code:
Public Property Text as String
    Set (Byval value as String)
        console.WriteLine(value)
        my.Computer.FileSystem.WriteAllText(logfile,now & value & vbnewline, true)
    End Set
    Get
        dim tmp as String = console.ReadLine ' auch sowas ist dann gleich möglich:wink:
        my.Computer.FileSystem.WriteAllText(logfile , " >" & tmp & vbNewLine,true)
        return tmp
    End Get
End Property
Sub Main()
    text = "Meldung 1"
    text = "Meldung 2"
    text = "Meldung 3"
End Sub
Diese Vorgehensweise lohnt sich insbesondere bei vergleichsweise vielen outputs.

Ist aber alles Andere als OOP-konform
 
GrafZahl, du hast noch eins vergessen:
Code:
5.times { puts "hello ruby!" }
Es hat durchaus auch Vorteile, wenn alles ein Objekt ist. Dann kann man selbst Zahlen Funktionen zuweisen, was es erlaubt, Code teilweise wie „natürliche Sprache“ zu lesen.
Ich lerne gerade Ruby, weil ich den Aspekt ganz hübsch finde.
Übersetzungsfunktionen kann man dann z.B. direkt dem nativen String zuweisen, a la
Code:
'hello world'.translate
oder Zahlen sehr einfach in (Hexadezimal-)Strings um wandeln:
Code:
29.to_s(16) # returns "1d"
Das sorgt auch in vielen Fällen dafür, dass man wenig in der Hilfe nachschauen muss, weil man durch Ausprobieren oft schon richtig liegt.
Da man die Klammern weglassen kann, hat man den Syntax Sugar quasi schon inklusive, da jede Funktion ohne Parameter aussieht wie eine einfache Variable. Es ist zudem gar nicht möglich, ohne Zugriffsmethoden auf klasseninterne Variablen zuzugreifen.

Der Vorteil bei der Verwendung von Zugriffsvariablen ist einfach, dass man als Klassenautor ein stabiles Interface bereitstellen kann, auch wenn man im Hintergrund Dinge ändert, und dass es genau definierte Orte (und wenn nötig, definierte Zeitpunkte -> Locks) gibt, an denen Variablen geändert werden. Das macht auch Debugging in einigen Fällen deutlich einfacher.
 
GrafZahl, du hast noch eins vergessen:
Code:
5.times { puts "hello ruby!" }
Es hat durchaus auch Vorteile, wenn alles ein Objekt ist. Dann kann man selbst Zahlen Funktionen zuweisen, was es erlaubt, Code teilweise wie „natürliche Sprache“ zu lesen.
Wie gesagt, 100% OO würde bei Zahlen keine Punkt-Vor-Strich berücksichtigen können, da 1 + 2 * 3 im OO Modell eigentlich 1.add(2).mul(3) (bzw. in Ruby "ohne Sugar" 1.send('+',2).send('*',3) ) entspricht. Das wird eher von Smalltalk und afaik Self wirklich konsequent umgesetzt, möchte aber kaum jemand tatsächlich haben ;)

Das sollte im übrigen nur ein Gedankenanstoss sein, da "modernere" OO Sprachen (Smalltalk/Self sind ziemlich alt. Von Self VMs hat Sun damals bei Java-Entwicklung die eine odere andere VM Technik "abgeschaut") bewusst auf "OO bis ins letze Detail" verzichten ;)
[/OT]

[Nochmehr OT]
Da man die Klammern weglassen kann, hat man den Syntax Sugar quasi schon inklusive, da jede Funktion ohne Parameter aussieht wie eine einfache Variable.
Den Sugar mit klammerlosen Parametern erkauft man sich aber damit, dass nicht zwischen Method Call und Method Lookup unterschieden werden kann ;)
Dafür ermöglicht es letztendlich "alles ist ein Ausdruck" Philosophie (was imho das viel "coolere" an Ruby ist, als die OO Umsetzung)
[ /nochmehr OT]
 
Zurück
Oben