Sichtbarkeiten und externe Klasse

Timo2012

Mitglied
Hallo Leute,

ich habe mich neu in diesem Forum angemeldet, da ich bei der Umsetzung eines OpenGL-Projektes ein wenig Hilfe benötige. Ich erstelle eine Simulation von einem Objekt, dass der Benutzer mittels verschiedener Methoden steuern soll. Dazu werden in seiner Klasse, die er dafür selbst schreiben muss bestimmte Methoden aufgerufen in denen von mir bereitgestellte Methoden benutzt werden sollen.
(Im Grunde so wie das AntMe-Projekt, falls es jemand kennt)
Un zwar möchte ich eine jar-Datei erstellen, die eine Main-Funktion hat und alle Funktionen der Simulation bereitstellt. Der Benutzer soll davon ja gar nichts mitbekommen. Er bekommt lediglich die jar-Datei und er soll seine eigene Klasse erstellen, die von einer bestimmten Basisklasse innerhalb der jar-Datei erbt.

  1. Die Basisklasse selbst stellt noch viel mehr Methoden bereit von denen der Benutzer nichts wissen soll. Wie kann man sinnvoll den Zugriff darauf unterbinden? Ich habe meine Interfaces in abstrakte Klassen umgeschrieben, die Methoden auf paketsichbar gestellt und alle Klassen in ein Package geklatscht. So ganz gefällt mir das vom Design her nicht! Gibt es da andere sinvollere Alternativen?
  2. Wie kann ich es realisieren, dass meine Simulation, die ja eigentlich die Klasse des Benutzers kennen muss, in der jar-Datei ohne die Klasse des Benutzers auskommt? Die Idee ist, dass die jar-Datei selbst executeable ist und die Klasse des Benutzers mehrfach für die Simulation instanziiert.

Es wäre nett, wenn mir jemand einen Gedankenanstoß geben könnte. Vielen Dank!

Timo
 
Ich habe meine Interfaces in abstrakte Klassen umgeschrieben
Ziemlich dumme Idee, wer hat dir den Quatsch denn beigebracht. Interfaces haben auch trotz abstrakter Klassen durchaus ihre Berechtigung.
die Methoden auf paketsichbar gestellt und alle Klassen in ein Package geklatscht.
Alles in EIN Packet? Das ist wirklich nicht optimal, du soltest dir schon eine Ordnderstrultur überlegen, welche zu deinen Klassen passt.

An der Stelle wäre ein UML Diagramm hilfreich, damit man einen Überblick über das Programm bekommt. Definitv aber kein Anfänger-Thema.

Wie kann ich es realisieren, dass meine Simulation, die ja eigentlich die Klasse des Benutzers kennen muss, in der jar-Datei ohne die Klasse des Benutzers auskommt?
Versteh ich nicht ganz. Es goibt eine Klasse Benutzer, welche für die Simulation gebraucht wird. Führt man die Simulation aus, soll aber die Simulation ohne einen Benutzer auskommen? Also wie jetzt? Suchst du sowas wie einen StandardBenutzer? Dann bau dir halt ein Interface Benutzer und implementiere es mit ´ner Standardimplementierung.
 
Zuletzt bearbeitet:
Hey Beere,

schon mal vielen Dank für die schnelle Antwort.

Ziemlich dumme Idee, wer hat dir den Quatsch denn beigebracht. Interfaces haben auch trotz abstrakter Klassen durchaus ihre Berechtigung.

Nunja, ich habe schon ein bisschen gegoogelt und diese "Lösung" gefunden. Dennoch soll der Benutzer ja nicht die Methoden sehen, die die Klassen implementieren. Das soll ja nur in der jar-Datei sichtbar sein. Ein Interface ist ja per se public. Genau das möchte ich nicht. Quasi public in meiner jar-Datei (Package), aber private für den Benutzer.

Alles in EIN Packet? Das ist wirklich nicht optimal, du soltest dir schon eine Ordnderstrultur überlegen, welche zu deinen Klassen passt.

An der Stelle wäre ein UML Diagramm hilfreich, damit man einen Überblick über das Programm bekommt. Definitv aber kein Anfänger-Thema.

Anbei ein stark vereinfachtes UML-Diagramm welches meine aktuelle Situation beschreibt.

Kurze Info dazu:
Die BasisKlasse stellt interne! Methoden +Attribute bereit von denen der User einige nutzen darf andere nicht! Er soll von dieser erben damit er diese erweitern und mit Leben füllen kann. Die SimulationControl soll aber nur die Klasse des Benutzers instanziieren. Meine OpenGL-Klasse nutzt dann nur Funktionen die die BasisKlasse bereitstellt.
 

Anhänge

  • Klassendiagramm.jpg
    Klassendiagramm.jpg
    45,7 KB · Aufrufe: 34
Zuletzt bearbeitet:
Ein Interface ist ja per se public.
Nö, is es erstmal nicht. Du kannst es auch packetweit deklarieren (also ohne Angabe des Bereichs), dann können es auch nur Klassen im selben Packet implementieren. Die Methoden sind dann aber natürlich public, das stimmt schon.
 
Ich hab irgendwie ein Problem damit, dass die main-Klasse ihren Benutzer kennen muss, um ausgeführt werden zu können. Du willst auf deine jar clicken und dann soll das Programm gestartest werden, das ist klar. Aber welche Informationen des Benutzers willst du dann innerhakb des Programmes verwenden? Klingt mir irgendwie unlogisch...

Also meines Erachtesn hast du zwei Möglichkeiten:
1. Wenn du die jar ausführbar machen willst, brauchst du natürlich urgendwo eine main-Methode, das ist klar. Da du dieser natürlich nur schwer irgendwelche komplexen Parameter mitgeben kannst, müsstest du deine Nutzerdaten in eine externe (Text-)Datei schreiben und dann im Programm auslesen (bzw. sogfern noch nicht existent eine neue anlegen).
oder 2. Du erstellst deine Nutzerklasse ganz normal und baust dir eine Instanz der Main-Klasse, mit der du ganz normal arbeitest (Doppelklick auf die jar geht dann natürlich wegen der fehlenden main nicht).
 
Zuletzt bearbeitet:
Bspw. soll das Projekt in Eclipse o.ä. eingefügt werden. Der Benutzer werkelt nur an seiner Klasse herum, Eclipse kompiliert dann seine Klasse und startet die Jar-Datei, welche die Benutzer-Klasse.class nutzt.
Das wäre die Logik. Vielleicht funktioniert sowas nicht mit Java?
Vielleicht benötigt die BenutzerKlasse eine main in der die Simulation aufgerufen wird? <- Es wäre mir persönlich lieb, wenn man das vermeiden könnte.
 
Also meines Erachtesn hast du zwei Möglichkeiten:
1. Wenn du die jar ausführbar machen willst, brauchst du natürlich urgendwo eine main-Methode, das ist klar. Da du dieser natürlich nur schwer irgendwelche komplexen Parameter mitgeben kannst, müsstest du deine Nutzerdaten in eine externe (Text-)Datei schreiben und dann im Programm auslesen (bzw. sogfern noch nicht existent eine neue anlegen).

Wie kann denn meine Jar-Datei den Quellcode vom Benutzer lesen, kompilieren und desen DatenTyp (also Klasse) für eine Instanziierung nutzen?
 
Jetzt bin ich endgültig verwirrt. Um was für Nutzerdaten geht es denn? Der Knackpunkt ist doch: willst du die jar direkt ausführen oder reicht es dir, eine Instanz der Main-Klasse zu erzeugen und mit der zu arbeiten.
Denn im zweiten Fall wäre sowas möglich:

Java:
class Benutzer implements IBenutzer {

    Benutzer() {
        Main mainClass = new Main(this);
        main.execute();
    }
}
Dann hätte deine Main-Klasse alle Informationen des Benutzers da und könnte damit arbeiten.

Aber Quellcode kannst du natürlich auch lesen und kompilieren. Musst nur dann dynamisch die Klasse per ClassLoader nachladen, aber wie das genau funktioniert, kann ich dir auf Anhieb jetzt nicht sagen, sollte aber etwa so gehen:
Java:
Class.forName("namedeinerBenutzerKlasse");
 
Zuletzt bearbeitet:
Auch für dich noch mal : das "I" als Präfix für ein Interface ist Java eher unüblich und auch in den Conventions wird davon abgeraten. Oder nutzt du für eine abstract auch "A" als Präfix ? Denk mal drüber nach.
 
auch in den Conventions wird davon abgeraten
Hmmmm... würde mich interessieren, wo du diese Info herhast. Ich hab mal das Netz durchstöbert und nix dergleichen gefunden:
Java Code Conventions hat gesagt.:
http://java.sun.com/docs/codeconv/CodeConventions.pdf
Interface names should be capitalized like
class names.
Da steht nichts weiter (weder für noch gegen das I)
Bei AboutJava.com heißt es dagegen:
Note that some programmers like to distinguish interfaces by beginning the name with an "I":

Also wenn du das nächste Mal mit angeblichem Wissen kommst, dann doch bitte auch mit einer Angabe, worauf das basiert...
 
Zuletzt bearbeitet:
Okay, das mit dem Laden werde ich mal ausprobieren (googeln).
Und was schlagt ihr bei den Sichtbarkeiten vor? BasisKlasse hat funktionen die von anderen packages genutzt werden sollen, aber nicht vom User der davon erbt! Er soll ja nur bestimmte Methoden sehen und benutzen.
 
Nun die BasisKlasse stellt Methoden und Attribute für jedes Objekt bereit welches simuliert werden soll.
Es werden in jedem Simulationsschritt passende Methoden aus der BasisKlasse aufgerufen. Der Benutzer soll diese aber mit Leben füllen und Methoden nutzen um sein eigenes Objekt zu steuern. Mit dynamischen Binden wird ja die tatsächliche Methode des Benutzers ausgewählt und nicht die leere der BasisKlasse. Von daher macht eine Vererbung ja schon sinn.
 
Also du hast ein Basisobjekt mit verschiedenen Methoden, von denen

- einige vom Benutzer überschrieben werden müssen

- andere im Simulationsschritt aufgerufen werden sollen, die der Benutzer aber nicht sehen, ausführen oder überschreiben darf

Sehe ich das so richtig?

Das klingt mir danach, dass ich diese Dinge trennen würde und dem Benutzer nur ein Interface gebe, das er implementieren muss.

Wenn du beides in einem willst, dann erbe einfach nochmal von der Klasse, die der Benutzer anlegt und greife dort auf die im Simulationsschritt benötigten Dinge zu.
 
Ihr stellt 2 jars bereit, einmal nur die api und einmal die implementierung.

Wer auch immer das nutzt soll zur entwicklungszeit das api jar einbinden und zur laufzeit die implementierung dazu geben.

Wenn ihr Kontrolle darueber habt ists natuerlich am einfachsten, wenn nicht, so koennt ihr nix anderes machen als dies zu kommunizieren.
 
Also du hast ein Basisobjekt mit verschiedenen Methoden, von denen

- einige vom Benutzer überschrieben werden müssen

- andere im Simulationsschritt aufgerufen werden sollen, die der Benutzer aber nicht sehen, ausführen oder überschreiben darf

Sehe ich das so richtig?

Das klingt mir danach, dass ich diese Dinge trennen würde und dem Benutzer nur ein Interface gebe, das er implementieren muss.

Wenn du beides in einem willst, dann erbe einfach nochmal von der Klasse, die der Benutzer anlegt und greife dort auf die im Simulationsschritt benötigten Dinge zu.


Jop, das hast du richtig verstanden. Interface wäre schlecht, denn ihm werden ja in der Klasse Methoden zur Verfügung gestellt. Davon erben wäre eigentlich auch nicht super, da die Simulation an sich Daten bereitstellt die der Benutzer beeinflussen kann. Zudem operieren die ihm bereitgestellten Methoden auf diesen Daten.
 
Dann nimm halt ´ne abstrakte Klassen, bei der du die zu implementierenden Methoden nur aufrufst:

Java:
abstract class AbstractUser {
    void simulate() {
        doSomething();
    }

    abstract void doSomething();
}

Java:
class User extends AbstractUser{
    @Override
    void doSomething() {
        ...
    }
}
Und wenn du dann noch willst, dass
Code:
doSomething()
nicht mehr von außen aufgerufen werden kann, machste sie halt noch
Code:
protected
. Dann hat der Nutzer deiner Klasse von außen nur die
Code:
simulate()
-Methode zur Verfügung, die intern aber
Code:
doSomething()
aufruft (welche du auch noch selbst implementieren kannst).
 
Zuletzt bearbeitet:
Jop, das hast du richtig verstanden. Interface wäre schlecht, denn ihm werden ja in der Klasse Methoden zur Verfügung gestellt. Davon erben wäre eigentlich auch nicht super, da die Simulation an sich Daten bereitstellt die der Benutzer beeinflussen kann. Zudem operieren die ihm bereitgestellten Methoden auf diesen Daten.

Also war meine Darstellung nicht vollständig. Der Benutzer soll auch noch auf eine Basisfunktionalität zugreifen können.

Also so in der Art

Basis, auf der der User seine Klasse aufbauen soll:

Java:
public abstract class UserBase {

    protected void useMe() {
        System.out.println("UserBase#useMe()");
    }

    protected void doNotUseMe() {
        System.out.println("UserBase#doNotUseMe()");
    }

    public abstract void implementThis();

}


Beispielimplementation:

Java:
public class UserObject extends UserBase {

    @Override
    public void implementThis() {
        System.out.println("UserObject#implementThis() start");
        useMe(); // erlaubt
        doNotUseMe(); // verboten
        System.out.println("UserObject#implementThis() end");
    }

}

Simulation:

Java:
package UserMethod;

public class Simulation {

    public Simulation() {
        UserObject u = new UserObject();
        u.doNotUseMe();
        u.implementThis();
    }

    public static void main(String[] args) {
        new Simulation();
    }

}

So, ja? Und wir wollen jetzt erreichen, dass die Simulation
Code:
doNotUseMe()
aufrufen kann, der Benutzer aber nicht, richtig?
 
Falls ja könnte man das so entheddern:


Basis, auf der der User seine Klasse aufbauen soll:

Java:
public abstract class UserBase {

    protected void useMe() {
        System.out.println("UserBase#useMe()");
    }

    public abstract void implementThis();

}


Beispielimplementation:

Java:
public class UserObject extends UserBase {

    @Override
    public void implementThis() {
        System.out.println("UserObject#implementThis() start");
        useMe(); // erlaubt
        //doNotUseMe(); // verboten - hier Compilerfehler!
        System.out.println("UserObject#implementThis() end");
    }

}

Simulationsbasis:

Java:
public class SimulationBaseFunctionality {

    protected void doNotUseMe() {
        System.out.println("SimulationBaseFunctionality#doNotUseMe()");
    }

}

Simulation:

Java:
public class Simulation {

    public Simulation() {
        SimulationBaseFunctionality base = new SimulationBaseFunctionality();
        UserObject u = new UserObject();
        base.doNotUseMe();
        u.implementThis();
    }

    public static void main(String[] args) {
        new Simulation();
    }

}


[EDIT]Das mit der dynamischen Erzeugung hab ich hier erstmal rausgelassen.[/EDIT]
 
Hi, okay. Aber SimulationBaseFunctionality und UserBase arbeiten mit ihren Methoden auf Daten von UserBase. Ein trennen in unterschiedliche Klassen macht m.E. keinen Sinn.
 
Dann musst du diese Daten nur so vorhalten, dass beide Klassen daran kommen, und zwar an die gleichen Objekte. Wenn du die aber vor dem Benutzer verstecken willst, dürfen sie nicht in der Basisklasse sein.

Wenn du in der Basisklasse ein [c]int x[/c] hast, wird der Benutzer auch auf es zugreifen können, es sei denn du machst es privat, dann kommt aber auch die Simulation nicht daran. Du musst dir also etwas schlaues für die Datenorganisation überlegen. Das hängt ein wenig davon ab, wie dein reales Projekt nun aussieht.

[EDIT]Ich dachte der Benutzer soll eine Art Algorithmus schreiben. Auf echte Objekte muss er doch dabei gar nicht zugreifen.[/EDIT]
 
Zuletzt bearbeitet:
Naja schon, so wie ich es bisher habe:

Alle Klassen die darauf arbeiten sind zusammen in einem Package.
OpenGL, SimulationControl, BasisKlasse sind in einem Package. Die Methoden die der User nicht verwenden darf sind paketsichbar. Die die er verwenden darf entweder public oder protected.

Leite ich nun aus einem anderen Package von BasisKlasse ab, so stehen mir nur die Methoden zur Verfügung die der Benutzer auch benutzen soll.

Mir gefällt es aber nicht in meinem Fall etwa 10 Klassen in einem Package zu haben, die in gewisser Weise nicht sehr viel miteinander zu tun haben. Ich würde bspw. gerne subpackages erstellen. Aber dann funktioniert das leider mit dem Zugriff nicht mehr :-(
 
Ich versuch, meinen Beitrag von 13:57 nochmal in Worte zu fassen:

Du möchtest eine Klasse
Code:
Nutzer
bauen, die von einer Basisklasse
Code:
BasisNutzer
erbt, wobei sie jedoch gewisse Methoden dieser überschreibt bzw. überhaupt erstmal definiert. Ein solcher
Code:
BasisNutzer
soll eine Simulation starten können, wobei die genaue Vorschrift dieser Simulation nicht innerhalb deiner API festgelegt ist, sondern von deiner
Code:
Nutzer
-Klasse. Außerdem soll diese Simulationsmethode nicht von außen ausfürhbar sein, sprich wenn jemand die Klasse Nutzer instanziiert, soll die Simulationsmethode ihm nicht zur Verfügung stehen, er soll nur die in Nutzer definierte Simulation starten dürfen.

Hab ich´s jetzt?

Also folgendes:
Code:
BasisNutzer
ist abstrakt mit der abstrakten Methode
Code:
simulate()
. Damit sie von außen nicht zugänglich ist natürlich
Code:
protected
.
Code:
Nutzer
erbt von
Code:
BasisNutzer
und definiert dabei durch die
Code:
simulate
-Methode, was genau gemacht werden soll. Da diese protected ist, kann sie nur innerhalb der Klasse
Code:
Nutzer
verwendet werden.

[EDIT]Achja, die Logik, nach welcher ein
Code:
BasisNutzer
ein
Code:
Simulationsobjekt
ist, musst du mir mal erklären.[/EDIT]
 
Zuletzt bearbeitet:
Nein nicht ganz. Ich vermute ich drücke mich hier unsauber aus.
Stellen wir und mal vor:

Klasse BasisAuto:
Kann gerendert werden -> methode render()
Kann fahren -> drive()

Der Reifen kann platzen, wenn das passiert, dann ruft die SimulationControl die entsprechende Methode der Klasse BasisAuto auf. Diese ist aber in BasisAuto nicht definiert; das soll der User machen.

Der erbt also von BasisAuto, sieht dann aber methoden wie render(). Der kann damit Unfug anstellen, soll er aber nicht. Nun soll nur die render Methode versteckt werden. Es gibt natürlich noch viel viel mehr Methoden, die der User nicht sehen und vor allem ausführen darf.

Also, wenn wir da so drüber diskutieren, dann fehlt mir ein wenig die friend Erklärung von c++. Das ist hier nicht möglich und somit kann ich m.E. nur die Klassen in ein Package packen. Und.. (Erklärung siehe oben)

Von daher denke ich es ist eine praktikable, wenn auch nicht ganz schöne Lösung. Aber was erlauben Strunz, ähh mir die java-Sprachkonstrukte denn anderes?

Ich denke ich lasse es so und kümmere mich erst einmal um das dynamische Klassenladen.

Über Zustimmung und vor allem Kritik würde ich mich freuen 🙂

Danke an alle!
 
Trennung der Dinge ist schon der richtige Weg. Das muss nicht alles in deinem Autoobjekt stecken, ein Renderobjekt kann auch mit einem Autoobjekt gefüttert werden und stellt es dann dar.
 
Ich versuch, meinen Beitrag von 13:57 nochmal in Worte zu fassen:

Du möchtest eine Klasse
Code:
Nutzer
bauen, die von einer Basisklasse
Code:
BasisNutzer
erbt, wobei sie jedoch gewisse Methoden dieser überschreibt bzw. überhaupt erstmal definiert. Ein solcher
Code:
BasisNutzer
soll eine Simulation starten können, wobei die genaue Vorschrift dieser Simulation nicht innerhalb deiner API festgelegt ist, sondern von deiner
Code:
Nutzer
-Klasse. Außerdem soll diese Simulationsmethode nicht von außen ausfürhbar sein, sprich wenn jemand die Klasse Nutzer instanziiert, soll die Simulationsmethode ihm nicht zur Verfügung stehen, er soll nur die in Nutzer definierte Simulation starten dürfen.

Hab ich´s jetzt?

Also folgendes:
Code:
BasisNutzer
ist abstrakt mit der abstrakten Methode
Code:
simulate()
. Damit sie von außen nicht zugänglich ist natürlich
Code:
protected
.
Code:
Nutzer
erbt von
Code:
BasisNutzer
und definiert dabei durch die
Code:
simulate
-Methode, was genau gemacht werden soll. Da diese protected ist, kann sie nur innerhalb der Klasse
Code:
Nutzer
verwendet werden.

[EDIT]Achja, die Logik, nach welcher ein
Code:
BasisNutzer
ein
Code:
Simulationsobjekt
ist, musst du mir mal erklären.[/EDIT]


Es gibt statische Objekte die einfach nur gerendert werden -> Renderable
und Objekte die zeitlich und ereignisgesteuert reagieren -> SimulationObjekt (Diese Klasse ist abstrakt, da diese noch weitere Methoden und Attribs bereit stellt, die sämtliche Klassen benötigen, die von ihr erben)
 
Trennung der Dinge ist schon der richtige Weg. Das muss nicht alles in deinem Autoobjekt stecken, ein Renderobjekt kann auch mit einem Autoobjekt gefüttert werden und stellt es dann dar.

Mit jeder weiteren Spezialisierung kommen weitere Funktionalitäten und Eigenschaften eines Objektes hinzu. Genauso wie ich es bisher auch gemacht habe. Zudem sollten die Daten und Eigenschaften ja auch genau in diesem Objekt gekapselt sein. Also von OO-Sicht mache ich es m.E. schon richtig. Mir missfällt nur die eingeschänkte Steuerung der Sichtbarkeit. Das macht auch aus OO-Sicht sinn, denn ich statte mein Objekt mit Funktionalität aus die der Benutzer dann doch nicht verwenden darf, die aber doch da ist! Es geht ja nur darum den Faktor Mensch mit all seinen Fehlern und dem Drang zu Zerstören ;-) die Hände zu binden. Und nach außen eine Grundfunktionalität bereitzustellen.
 
Der Reifen kann platzen, wenn das passiert, dann ruft die SimulationControl die entsprechende Methode der Klasse BasisAuto auf. Diese ist aber in BasisAuto nicht definiert; das soll der User machen.
Und was für eine Methode ist das dann?
Code:
wechsleRad()
oder sowas, vlcht. Stellt sich mir aber die Frage, warum diese Methode nicht im
Code:
BasisAuto
implementiert wird, geht man davon aus, dass sich das Wechseln von LKW- und PKW-Rädern nicht allzu stark unterscheidet, aber da fehlt mir der Hintergrund, ist auch Wurscht. Also lassen wir halt die Implementierung außen vor und überlassen es dem Nutzer der Klasse, was er damit macht (die Methode wird also abstrakt).

Der erbt also von BasisAuto, sieht dann aber methoden wie render(). Der kann damit Unfug anstellen, soll er aber nicht. Nun soll nur die render Methode versteckt werden. Es gibt natürlich noch viel viel mehr Methoden, die der User nicht sehen und vor allem ausführen darf
Da hilft nur eine gute Doku, oder eben die Methoden zum rendern private machen, was aber auch nicht Sinn der Sache ist. Aber Methoden, die du nicht direkt aufrfen sollst, gibt´s auch in der JAVA-Api genug,
Code:
Thread#run()
ist hier ein Bsp. oder
Code:
Component#getGraphics()
. Da kommt es nur auf die Kommunkation an, weil du das eben nicht bis ins letzte Detail verhindern kannst.

[EDIT]Wenn du die
Code:
render
-Methode vor´m Überschreiben schützen willst, kannst du sie auch einfach
Code:
final
machen, dann kann sie im schlimmsten Fall "nur noch ausgeführt" aber nicht verändert werden.[/EDIT]
 
Zuletzt bearbeitet:
Wenn du Bösartigkeit verhindern willst, denke daran, dass man mit Reflections auch an private Elemente der Basisklasse kommt.
 

Neue Themen


Zurück
Oben