[OOP] private properties und ihre Sichtbarkeit

#1
Guten Morgen

Ich bin gerade über ein, meiner Meinung nach, etwas seltsames Verhalten der gängigen OOP-Implementation gestolpert. Es handelt sich um die Sichtbarkeit von privaten Properties einer Klasse.
Ich war der Meinung, falls eine Eigenschaft als privat deklariert wurde, sollte dies auch in jedem Kontext eingehalten werden. Doch wurde ich eines besseren belehrt.
Nun, das einfachste ist wohl ein Beispiel (in PHP, Java scheint sich gleich zu verhalten).

PHP:
<?php

class Something
{
    private $x;
    
    public function __construct()
    {
        $this->x = 1;
    }
    
    public function foo(Something $bar)
    {
        echo $bar->x;
    }
    
    public function bar(SomethingOther $foo)
    {
        echo $foo->a;
    }
}

class SomethingOther
{
    private $a;
    
    public function __construct()
    {
        $this->a = 1;
    }
}

$meep = new Something();
$asdf = new Something();
$qwer = new SomethingOther();

//will throw a fatal
$meep->bar($qwer);

//will output: 1
$meep->foo($asdf);

?>
Nun wie man sieht ist für das Objekt meep das private Property $a der Klasse SomethingOther nicht sichtbar. Soweit so gut.
Falls jedoch ein Objekt auf eine private Eigenschaft eines Objekts derselben Instanz zugreifen will, so funktioniert das.
Dies obwohl wir uns in $meep definitiv in einem "äusseren" Kontext (im Bezug auf das Objekt $asdf) befinden.

Nun, auf die Frage: "Wie konnte ich mit meinem Verständnis nur so falsch liegen?" werde ich wohl nie eine Antwort finden. Doch hoffe ich ihr könnt mir sagen, warum dieses Verhalten Sinn macht - und vor allem: Ist das wirklich keine Verletzung der allgemeinen OOP-Spezifikation?

Ich danke im Voraus.
sheeep

Edit:
Code:
#include <stdio.h>

using namespace std;

class Something
{
	private: 
	int x;
	
	public:
	Something();
	void foo(Something &b);
};

Something::Something()
{
	this->x = 1;
}

void Something::foo(Something &b)
{
	printf("%d\n", b.x);
}

int main( int argc, const char* argv[] )
{
	Something a = Something();
	Something b = Something();
	
	a.foo(b);
}
 
Zuletzt bearbeitet:
#3
Öhm bei mir schon.

Code:
$> java -version
java version "1.6.0_0"
OpenJDK Runtime Environment (IcedTea6 1.4.1) (6b14-1.4.1-0ubuntu12)
OpenJDK Server VM (build 14.0-b08, mixed mode)
Code:
import java.util.*;

public class App
{
    public static void main(String[] args){
        Something a = new Something();
        Something b = new Something();
        
        a.som(b);
        
    }
}

Code:
import java.util.*;

public class Something{
    private int x;

    public Something(){
        this.x=1;
    }
    
    public void som(Something som){
        System.out.println(som.x);
    }
}

Edit: Sorry folgendes hab ich überlesen.
The field SomethingOther.a is not visible
Nicht verwunderlich. SomethingOther ist eben etwas anders ^^
Der Kern meiner Frage bezieht sich allerdings aufs private-Handling von Objekten desselben Typs.
 
Zuletzt bearbeitet:
#7
Ich hab das mal hier in die Runde geworfen und als Antworten "Ruby" und "Smalltalk" bekommen. Ich kann zu beiden nix sagen, aber zumindest bei Smalltalk kann ich mir das wirklich gut vorstellen.
 
#8
Nun, ich bein kein Held in Ruby (ehrlich gesagt sind ist das mein "first attempt"), daher weiss ich auch nicht, wie man in Ruby private Properties definiert. Allerdings hab ich kurz verifiziert, dass selbiges auch mit Methoden funktioniert.. Nun t3rr0r.bYt3 schien richtig zu liegen:

Code:
# ..

class A
    public
    def b(a)
        a.d()
    end
    
    private
    def d()
        puts "w00t?"
    end
end

f = A.new
g = A.new
f.b(g)
Code:
sheeep@foobar:/home/sheeep/dev$ ruby m.rb
m.rb:6:in `b': [B]private method[/B] `d' called for #<A:0xb78908e8> (NoMethodError)
    from m.rb:17
Falls jemand, der Ruby verinnerlicht hat, mein Code verifizieren könnte, wäre ich dankbar. Und ernsthaft. Falls Ruby tatsächlich eine, meiner Meinung nach richtige Vorgehensweise implementiert hat, so lohnt es sich für mich definitiv mich mit dieser Sprache genauer auseinanderzusetzen.

Ach und, könnte sich ein Smalltalk-Jünger herablassen für mich die Frage zu klären? :D Würde mich wirklich interessieren.

Danke im Voraus.
sheeep
 

odigo

Member of Honour
#9
Ich muss zugeben, daß ich (und ein paar meiner Kollegen) doch etwas überrascht war. Ich war bis jetzt fest der Meinung, daß private auf Objektebene gilt und nicht auf Klassenebene.

Sonderlich tragisch finden kann ich das aber trotzdem nicht 8)
 

xblax

Member of Honour
#10
Das macht schon Sinn, das Geheimnisprinzip wird dadurch ja auch nicht verletzt.
Eine Anwendung für die es Sinn macht ist zum Beispiel sowas hier http://de.wikipedia.org/wiki/Trie (Wenn man da von einem Element aus den ganzen Baum durchläuft beispielsweise).
Protected macht das gleiche, nur dass auch noch Kind-Klassen darauf zugreifen können.

Noch was ganz alltägliches (Java):
Code:
class Something {
    
    private int a;

    Something(int a) {
        this.a = a;
    }

    public boolean equals(Something other) {
        if(this.a == other.a) {
            return true;
        } else {
            return false;
        }
    }
}
In C++ kann man dann stat der equals()-Methode den == Operator überladen, aber das funktioniert ansich genauso. Ohne diese Funktionalität könnte man sowas gar nicht implementieren und es wäre unmöglich die Gleicheit zweier Objekte zu bestimmen.

Bei Wikipedia ist es übrigens auch genau so definiert:
http://de.wikipedia.org/wiki/Datenkapselung_(Programmierung)
 
Zuletzt bearbeitet:
#11
Das macht schon Sinn, das Geheimnisprinzip wird dadurch ja auch nicht verletzt.
Das ist, wie ich finde, wieder nur Auslegungssache. Ich sehe kein Problem darin, sämtliche Zustände wirklich objekt-privat zu gestalten (Ausnahme: Vererbung), und den Rest über Methodenaufrufe zu gestalten. Natürlich bräuchte man dann wieder Accessor für die Variablen, aber die könnten dann eben z.b. klassen-privat sein.
Es ist halt ein eher pragmatischer Ansatz, den Zustand klassen-privat zu gestalten, da der Entwickler der Klasse kaum mit seinen eigenen Variablen Schindluder treiben wird (oder anders gesagt: gegenüber dem Rest bleibt man abgeschottet).
 

xblax

Member of Honour
#12
Das ist, wie ich finde, wieder nur Auslegungssache. Ich sehe kein Problem darin, sämtliche Zustände wirklich objekt-privat zu gestalten (Ausnahme: Vererbung), und den Rest über Methodenaufrufe zu gestalten. Natürlich bräuchte man dann wieder Accessor für die Variablen, aber die könnten dann eben z.b. klassen-privat sein.
Es ist halt ein eher pragmatischer Ansatz, den Zustand klassen-privat zu gestalten, da der Entwickler der Klasse kaum mit seinen eigenen Variablen Schindluder treiben wird (oder anders gesagt: gegenüber dem Rest bleibt man abgeschottet).
Das macht aber bei genauerem Nachdenken keinen Sinn, denn wenn ich überhaupt dazu in der Lage bin die Funktionaliät auf private Variablen anderer Objekte der selben Klasse zu zu greifen zu nutzen, bin ich folglich der Klassenautor. Dann kann ich aber auch nach Lust und Laune setter/getter erstellen oder gleich die Sichtbarkeiten ändern, also würde es keinen weiteren Sicherheitsgewinn geben.
 
#13
Ich rede auch nirgends von einem Sicherheitsgewinn. Ich sehe lediglich kein Problem darin, eine Sprache so zu gestalten, dass ihre Objekte ausschließlich objekt-private Attribute besitzen, und alles andere über Methoden zu handhaben. Es ist höchstens unpraktisch, aber nur konsequent.
denn wenn ich überhaupt dazu in der Lage bin die Funktionaliät auf private Variablen anderer Objekte der selben Klasse zu zu greifen zu nutzen, bin ich folglich der Klassenautor
Nein, ich kann evtl. auch Reflection benutzen. Es spielt keine Rolle, wer ich bin, sondern eben dass ich dann vollen Zugriff auf den internen Zustand des Objekts habe. Aber ich glaube, wir reden hier sowieso aneinander vorbei :)
 
#15
Woah leute, Hirn einschalten. Ich hab doch niemals gesagt, dass man eine x-beliebige, existierende Sprache bequem umbauen kann.

/Edit: Ich hab mein Wissen über einen C++-Copy-Constructor aus der Wikipedia, was spricht dagegen, (mindestens klassen-sichtbare) Accessor auf alle Attribute zu definieren, und diese dann zu verwenden?
Außer natürlich, dass C++ den Overhead des Methodenaufrufs nicht haben will.
 
Zuletzt bearbeitet:

xblax

Member of Honour
#16
Naja ich halte es einfach für Sinnfrei ...
Code:
class Something {
    
    private int a;

    Something(int a) {
        this.a = a;
    }

    classprivate int getA() {
        return a;
    }

    public boolean equals(Something other) {
        if(this.a == other.getA()) {
            return true;
        } else {
            return false;
        }
    }
}
Es macht keinen Sinn die Implementierung vor sich selbst zu verstecken. Das ist ein Paradoxon. Wenn ich sowas wollte:
Code:
    classprivate int getA() {
        return 2*a;
    }
Könnte ich auch zielführender schreiben:
Code:
    public boolean equals(Something other) {
        if(this.a == 2*other.a) {
            return true;
        } else {
            return false;
        }
Auch wenn man sowas bei einer equals()-Methode nie machen sollte :D

Das mit den Reflections musst du mir nochmal erklären, da kenne ich micht nicht wirklich aus, aber das ist doch nichts, was den Zugriff auf eine private deklarierte Variable von außen erlauben sollte, oder?
 
#17
Jeez, immer diese Pragmatiker. Ob Sinn oder nicht, bestenfalls sind beide Varianten semantisch identisch, und klassen-privat statt objekt-privat bequemer.
Das ist lediglich eine Design-Entscheidung, ob man eben definiert, dass Objekte sich *ausschließlich* über Methodenaufrufe (Message Passing!) unterhalten, oder ob man so eine Einschränkung eben lockert.

Und ja, via Reflection kommt man an wirklich alles ran. Reflections sind auch die Begründung für so ziemlich alle Artikel im Netz zum Thema "Aber in Java sind Strings DOCH nicht immutable" (Wobei einem da sofort der SecurityManager ans Bein pisst, wenn er denn läuft).
 
Oben