Du verwendest einen veralteten Browser. Es ist möglich, dass diese oder andere Websites nicht korrekt angezeigt werden. Du solltest ein Upgrade durchführen oder ein alternativer Browser verwenden.
OOPWie sollte ich das organisieren (Abstract? Interface?)
ich bin gerade am Organisieren meines Java Projektes und es würde mir sehr viel bei meinem Verständnis für OOP weiterhelfen, wenn ihr mir sagen könnt, was die beste Lösung für meine Klassen sind.
Man habe mehrere Fischklassen (Hering, Goldfisch, Hai, usw.) und dazu natürlich eine Mutterklasse Fisch.
Es gibt ein paar Methoden, also Fähigkeiten, die ich meine Fischklassen zwingen möchte zu besitzen, also zu implementieren. Darum dachte ich zuerst ich sollte ein Interface machen.
Dann gibt es aber ein paar Sachen, die möchte ich nicht in jeder Fischklasse neu schreiben. Also wäre eine Abstrakte Klasse doch besser oder nicht?
Ich würde mich sehr über schnelle Unterstützung freuen!
Deine Anforderungen sind:
- Es muss etwas implementiert werden per Contract. Ergo ein Interface.
- Es muss nicht immer alles neu implementiert werden. Ergo eine abstrakte Klasse mit vordefiniertem Verhalten, dass von einem Interface kommt.
Ergänzend sollte noch erwähnt werden, dass dein Vorhaben nach einer "partiell abstrakten Klasse" klingt. Das heißt alle Methoden, die in den Unterklassen identisch sind, werden in der abstrakten Basisklasse implementiert und alles was die Unterklassen selbst implementieren müssen sind auch in der Basisklasse abstrakte Methoden.
Habe ich jetzt einen Verständnisfehler? Wieso redet ihr von einer abstrakten Klasse? Vererbung spezialisiert doch die Klassen, habe ich immer gedacht. Also würde ich die gemeinsamen Anforderungen in der Super-Klasse implementieren, die dann mit den Sub-Klassen spezialisiert werden.
Beispiel: In die Super-Klasse Betriebsangehoeriger die Attribute Name, Vorname, Alter, Eintrittsdatum und die dazugehörigen Methoden. Die Sub-Klasse Arbeiter erbt von Betriebsangehoeriger und wird um Arbeitsstunden und Stundenlohn erweitert. Die Sub-Klasse Angestellter erbt ebenfalls von Betriebsangehoeriger und wird nur um Monatsgehalt erweitert.
Habe ich jetzt einen Verständnisfehler? Wieso redet ihr von einer abstrakten Klasse? Vererbung spezialisiert doch die Klassen, habe ich immer gedacht. Also würde ich die gemeinsamen Anforderungen in der Super-Klasse implementieren, die dann mit den Sub-Klassen spezialisiert werden.
Beispiel: In die Super-Klasse Betriebsangehoeriger die Attribute Name, Vorname, Alter, Eintrittsdatum und die dazugehörigen Methoden. Die Sub-Klasse Arbeiter erbt von Betriebsangehoeriger und wird um Arbeitsstunden und Stundenlohn erweitert. Die Sub-Klasse Angestellter erbt ebenfalls von Betriebsangehoeriger und wird nur um Monatsgehalt erweitert.
Eine abstrakte Klasse ist für mich eine Klasse, die nur die Methodensignaturen enthält aber im Gegensatz zum Interface auch Attribute besitzt. In einer abstrakten Klasse ins die Methoden auch mit "abstract" deklariert, in einer "normalen" Super-Klasse aber nicht.
Eine abstrakte Klasse ist für mich eine Klasse, die nur die Methodensignaturen enthält aber im Gegensatz zum Interface auch Attribute besitzt. In einer abstrakten Klasse ins die Methoden auch mit "abstract" deklariert, in einer "normalen" Super-Klasse aber nicht.
Das ist etwas zu schwarz/weiß gedacht (sorry, SchwarzWeiß, das ist nur Zufall )
In einer abstrakten Klasse müssen durchaus nicht alle Methoden abstrakt sein, das wird in der Praxis auch meist nicht der Fall sein.
Gegenfrage. Wenn ich bei der TDD zwei Klassen haben, die gemeinsame Funktionalität haben. Dann kann ich diese Funktionalität in eine neue Klasse ziehen, von der die beiden anderen erben. Habe ich dann eine "abstrakte Klasse" erstellt? Ich denke "nein". Wenn "Arbeiter und "Angestellter" von "Beschaeftigter" erben, dann kann ich letzteres doch auch für Methoden als Parameter anwenden und auch Instanzen von ihm bilden. In diesem Fall doch, wenn ich z.B. eine Liste des Krankenstandes anfertigen will. Dann ist es doch egal, wie er Geld bekommt.
Wenn ich bei der TDD zwei Klassen haben, die gemeinsame Funktionalität haben. Dann kann ich diese Funktionalität in eine neue Klasse ziehen, von der die beiden anderen erben. Habe ich dann eine "abstrakte Klasse" erstellt?
Ja, wenn es Beschäftigte gibt, die weder Arbeiter noch Angestellter sind und keinen Lohn bekommen, dann ginge das - alternativ böte sich da die Unterklasse "Sklave" an...
Ja, aber krank ist trotzdem ein Arbeiter oder ein Angestellter. Die geben kaum ihr Vorhandensein als dies aus, werden für eine kurze Zeit Beschäftigter, und verwandeln sich danach wieder zurück.
dynamischer vs statischer Typ ist dir aber schon bekannt?
@mrBrown, ich kann dir nicht folgen. Interfaces und abstrakte Klassen sollen nach meinem Wissen ähnliches bewirken. Und das ist, dass sie Methoden vorgeben, die in Klassen, die davon erben oder sie einbinden, ausprogrammiert werden müssen. Sie sind also theoretisch Pausvorlagen für Klassen, die ja wiederum Pausvorlagen für Objekte sind. Wie hier schon geschrieben wurde, meine ich, dass abstrakte Klassen nur beerbt werden können, aber aus ihnen keine Instanzen gebildet werden können. Das ist bei Interfaces eben anders.
Wie hier schon geschrieben wurde, meine ich, dass abstrakte Klassen nur beerbt werden können, aber aus ihnen keine Instanzen gebildet werden können. Das ist bei Interfaces eben anders.
Hat auch niemand gesagt, dass man zwingend eine abstrakte Klasse draus machen muss...aber es muss eben auch keine normale sein.
Und für die Ursprungsfrage wäre es besserer Stil.
Ich versuche mal ein Beispiel, leider ist mir auf die Schnelle nichts besseres eingefallen:
Java:
public interface LogInterface
{
void logError(String errorstr);
}
public abstract class AbstractFileLogger implements LogInterface
{
public void setFileName(String fileName)
{
// code
}
public abstract void logError(String errorstr);
}
public class SimpleFileLogger extends AbstractFileLogger
{
@Override
public void logError(String errorstr)
{
// code
}
}
public class FormattingFileLogger extends AbstractFileLogger
{
@Override
public void logError(String errorstr)
{
// code
}
}
public class SomeClass
{
private LogInterface logger;
// die konkrete Implementation des Loggers ist nicht relevant...
public SomeClass(LogInterface logger)
{
this.logger = logger;
}
}
public static main()
{
SimpleFileLogger logger = new SimpleFileLogger();
logger.setFileName("simple.log");
SomeClass someInstance = new SomeClass(logger);
// weiterer Code
}
Das Interface legt fest, dass es eine Methode zum Loggen eines Strings geben soll.
In der abstrakten Klasse, werden Methoden implementiert, die die Verwaltung einer Datei in irgendeiner Weise vornehmen. Davon wiederum erben zwei konkrete Implementation für einen einfachen Logger, der den Text unformatiert in die Datei schreibt und eine zweite, die vorher noch irgendwelche Formatierungen vornimmt. Im eigentlichen Programm verlangen wir nur eine Klasse, die das Interface implementiert und sind dort von der konkreten Programmierung dieser Klasse unabhängig.
Es könnte irgendwann noch einen AbstractDatabaseLogger usw. geben, in der Klasse "SomeClass" ist keine weitere Anpassung dafür notwendig. Das ist der (oder ein) Sinn von Interfaces....
Hui hier hat sich ja einiges getan. Ihr seid jetzt in ne Diskussion über allgemeine Objektorientierung abgerutscht Ich würde gerne mal zu meinem konkreten Superklasse Fish, Subklassen HerringFish, PufferFish, SharkFish, usw. zurückkehren.
Nochmal zusammengefasst: Ich brauchte eine Lösung, bei der nur die Subklassen instanziierbar sind. Gleichzeitig gibt es Methoden, die jeder der Subklassen haben sollte.
Ihr empfahlt mir ein Interface (IFish), welche von einer abstrakten Superklasse Fish implementiert wird. Darauf habe ich jetzt die letzten Tage mein Programm aufgebaut.
Das ist nun etwas hässlich bei mir geworden. Ich meine ich kann damit leben, aber ich bin nicht sicher ob das im Sinne der OOP ist, was ich da gemacht habe.
Meine Subklassen siehen alle aus wie folgt: HerringFish.java
Java:
public class HerringFish extends Fish {
private static final int animationSpeed = 2000;
private static final int price = 50;
@Override
public void spawn() {
super.spawn("hering.png", 120, 41);
super.animate(animationSpeed);
}
public static int getPrice() {
return price;
}
public void special() {
super.special(animationSpeed, 350, 410);
}
}
Ziemlich arm. Imgrunde unterscheiden sich die Fische (Subklassen) nur durch ihre Konfiguration. Das heißt ich gebe immer nur seine Attributwerte an die Methoden der Superklasse weiter.
Java:
public interface IFish {
void spawn(String imagePath, int width, int height);
void animate(int speed);
static int getPrice() {
return 0;
}
void die();
Node getNode();
}
Java:
public abstract class Fish implements IFish {
protected ImageView iv;
protected double currentX;
protected double currentY;
protected PathTransition animation;
public void spawn() {}
@Override
public void spawn(String imagePath, int width, int height) {
Image image = new Image("/mia/res/img/" + imagePath);
iv = new ImageView();
// code code code
}
public void special() {}
public void special(int animationSpeed, int pipeX, int pipeY) {
// code code code
}
@Override
public void animate(int speed) {
animation = new PathTransition();
// code code code
animation.play();
}
public void stopAnimation() {
animation.stop();
}
private Path createPath() {
Path path = new Path();
// code code code
return path;
}
@Override
public void die() {
animation.stop();
iv.setImage(null);
}
@Override
public Node getNode() {
return iv;
}
private Path createPathToSpecial() {
Path path = new Path();
// code code code
return path;
}
}
In der Fish.java war ich dann immer wieder gezwungen 2x die gleiche Methode zu erstellen. Einmal die Methode mit dem eigentlichen Code (wie special(...) oder spawn(...)), die eben nur mit den Attributen der Subklasse funktionieren, welche ich halt als Parameter übergeben muss. Und dann muss ich die Methode mit dem gleichen Namen nochmal erstellen mit ohne Parameter und leer. Dies einfach aus dem Grund, dass ich im Programmcode nicht weiß mit welcher Fisch Subklasse ich es zu tun habe, und die Objekte als Fish deklariere. Es wäre mir nicht erlaubt auf diese Objekte .spawn() oder .special() auszuführen, wenn es diese Methode in der Fish Klasse nicht gibt.
Versteht ihr mich? Man ich steh total auf dem Schlauch...
Ziemlich arm. Imgrunde unterscheiden sich die Fische (Subklassen) nur durch ihre Konfiguration. Das heißt ich gebe immer nur seine Attributwerte an die Methoden der Superklasse weiter.
In der Fish.java war ich dann immer wieder gezwungen 2x die gleiche Methode zu erstellen. Einmal die Methode mit dem eigentlichen Code (wie special(...) oder spawn(...)), die eben nur mit den Attributen der Subklasse funktionieren, welche ich halt als Parameter übergeben muss. Und dann muss ich die Methode mit dem gleichen Namen nochmal erstellen mit ohne Parameter und leer. Dies einfach aus dem Grund, dass ich im Programmcode nicht weiß mit welcher Fisch Subklasse ich es zu tun habe, und die Objekte als Fish deklariere. Es wäre mir nicht erlaubt auf diese Objekte .spawn() oder .special() auszuführen, wenn es diese Methode in der Fish Klasse nicht gibt.
Das Interface sollte ALLE Methoden definieren, die alle Fische haben und die sichtbar sein sollen.
Die abstrakte Klasse definiert alle Attribute, die alle Fische haben, und implementiert alle Methoden, die nur diese Attribute nutzen. Für die Attribute gibt es entweder Setter, oder sie werden im Konstruktor gesetzt.
Die Unterklassen definieren dann Methoden und Attribute, die NUR jeweils sie selbst brauchen, und implementieren alle Methoden, die in der abstrakten Klasse nicht implementiert werden konnten.
In deinem Fall kann bereits alles in der Abstrakten Klasse implementiert werden, da sie sich nur in den Werten unterscheiden - du brauchst also nur Interface und Implementierung dessen.
Ja das stimmt. Aber ich hab ganz feste vier Fischtypen und dafür will ich allein schon aus Prinzip eigene Klassen. Das ist, ich weiß nicht ob ichs erwähnt hab, ein Hochschulprojekt wo Wert auf objektorientierte Strukturen gelegt wird. Ich möchte mir offen halten, für die Fischtypen besondere Eigenschaften oder Fähigkeiten einzubauen, was ich auch noch machen werde.
Die abstrakte Klasse definiert alle Attribute, die alle Fische haben, und implementiert alle Methoden, die nur diese Attribute nutzen. Für die Attribute gibt es entweder Setter, oder sie werden im Konstruktor gesetzt.
Argh... du öffnest mir die Augen. Ich kann ja die ganzen Attribute in der Fish.java haben und einfach im Konstruktor der Subklassen diese Attribute konfigurieren. (facepalm)
Die Unterklassen definieren dann Methoden und Attribute, die NUR jeweils sie selbst brauchen, und implementieren alle Methoden, die in der abstrakten Klasse nicht implementiert werden konnten.
Wie meinst du das. Ich kann doch wenn ich in einem Interface Methoden vorschreibe, doch nicht diese in der implementierenden Klasse NICHT implementieren und stattdessen dafür in einer von dieser erbenden?
In deinem Fall kann bereits alles in der Abstrakten Klasse implementiert werden, da sie sich nur in den Werten unterscheiden - du brauchst also nur Interface und Implementierung dessen.
Also wie ich oben erklärt habe, werde ich die Subklassen doch beibehalten. Aber ich stell diese Fragen trotzdem noch für mein OOP Verständnis.
Wieso würde ich ein Interface machen, wenn ich nur eine implementierende Klasse habe. Wieso mache ich die eine einzige Klasse die ich brauche nicht einfach so wie ich sie haben will?
Wieso habe ich das überhaupt, auch wenn ich die Subklassen habe, so gemacht? Wieso habe ich die abstrakte Klasse nicht einfach gebaut wie ich sie wollte? Sorry, mein Kopf arbeitet gerade nicht so gut.
Vielen Dank für deine schnelle Antwort, sie war wirklich sehr aufschlussreich.
Ja das stimmt. Aber ich hab ganz feste vier Fischtypen und dafür will ich allein schon aus Prinzip eigene Klassen. Das ist, ich weiß nicht ob ichs erwähnt hab, ein Hochschulprojekt wo Wert auf objektorientierte Strukturen gelegt wird. Ich möchte mir offen halten, für die Fischtypen besondere Eigenschaften oder Fähigkeiten einzubauen, was ich auch noch machen werde.
Wie meinst du das. Ich kann doch wenn ich in einem Interface Methoden vorschreibe, doch nicht diese in der implementierenden Klasse NICHT implementieren und stattdessen dafür in einer von dieser erbenden?
Wieso würde ich ein Interface machen, wenn ich nur eine implementierende Klasse habe. Wieso mache ich die eine einzige Klasse die ich brauche nicht einfach so wie ich sie haben will?
Wieso habe ich das überhaupt, auch wenn ich die Subklassen habe, so gemacht? Wieso habe ich die abstrakte Klasse nicht einfach gebaut wie ich sie wollte? Sorry, mein Kopf arbeitet gerade nicht so gut.
Na das war euer Vorschlag. Also auf der ersten Seite des Threads haben das mehrere gemeint. Und ich muss es damals ja auch irgendwie für sinnvoll erachtet haben.
Das ist aber eine ziemlich schräge Vorgehensweise. Du willst unbedingt eigene Klassen pro Fischart, also denkst du dir irgend etwas aus, damit das sinnvoll erscheint. Das kann leicht zu einem großen Reinfall werden, denn Anfänger neigen dazu, viel zu sehr auf Vererbung und verschachtelte Klassenhierarchien zu setzen. Woher weißt du denn jetzt schon, dass diese Spezialfähigkeiten überhaupt eigene Fisch-Unterklassen erforderlich machen? Vielleicht wäre ja auch eine Spezialfähigkeitsklasse pro Spezialfähigkeit besser. Was machst du, wenn mehrere Fischsorten dieselbe Spezialfähigkeit haben?
Es ist doch erst einmal ein Riesenproblem, wenn man pro Fischsorte eine eigene Klasse hat, weil man dann kaum jemals alle Fischsorten unterstützen kann. Solche Einschränkungen würde ich lieber vermeiden.
Die letzten zwei Sätze habe ich nicht verstanden, kannst du das bitte nochmal erklären?
Es ist einfach so. Das ganze Programm besteht aus einem Aquarium und einem Shop. Da gibt es vier verschiedene Fischarten zu kaufen. Die Fische sind was das ganze Spiel ausmachen. Es gibt fast nur sie. Da möchte ich schon eigene Klassen haben. Wir haben noch viel Zeit für das Projekt und ich möchte mir die Möglichkeit offen halten noch ganz verrückte Dinge mit den Fischen anzustellen und da ist es mir lieber ich habe die Fischarten in separaten Klassen. Ich mein die Alternative wäre doch ich instanziiere meine Heeringe als Fish mit einem Parameter "Heering" oder als Enum wie auch immer (Wie würdet ihr das denn machen, mal so aus Interesse). Und new Fish("Herring") zu machen, wo es 4 konkrete Fischarten gibt und diese das halbe Spiel ausmachen, finde ich einfach sehr unästhetisch.
Da neige schon irgendwie echt dazu Spezialfähigkeiten für die Fische zu erfinden damit das mehr Sinn macht. Was natürlich eine komische Vorangehenweise ist. Aber unter dem Aspekt, dass es nur ein Schulprojekt ist und wir programmieren können was immer wir wollen, passe ich mein Programm halt der objektorientierten Struktur meiner Wahl an, anstatt andersrum.
Die letzten zwei Sätze habe ich nicht verstanden, kannst du das bitte nochmal erklären?
Es ist einfach so. Das ganze Programm besteht aus einem Aquarium und einem Shop. Da gibt es vier verschiedene Fischarten zu kaufen. Die Fische sind was das ganze Spiel ausmachen. Es gibt fast nur sie. Da möchte ich schon eigene Klassen haben. Wir haben noch viel Zeit für das Projekt und ich möchte mir die Möglichkeit offen halten noch ganz verrückte Dinge mit den Fischen anzustellen und da ist es mir lieber ich habe die Fischarten in separaten Klassen. Ich mein die Alternative wäre doch ich instanziiere meine Heeringe als Fish mit einem Parameter "Heering" oder als Enum wie auch immer (Wie würdet ihr das denn machen, mal so aus Interesse). Und new Fish("Herring") zu machen, wo es 4 konkrete Fischarten gibt und diese das halbe Spiel ausmachen, finde ich einfach sehr unästhetisch.
Da neige schon irgendwie echt dazu Spezialfähigkeiten für die Fische zu erfinden damit das mehr Sinn macht. Was natürlich eine komische Vorangehensweise ist. Aber unter dem Aspekt, dass es nur ein Schulprojekt ist und wir programmieren können was immer wir wollen, passe ich mein Programm halt der objektorientierten Struktur nach Wunsch an, anstatt - wie es üblich wäre - andersherum.
Es ist doch erst einmal ein Riesenproblem, wenn man pro Fischsorte eine eigene Klasse hat, weil man dann kaum jemals alle Fischsorten unterstützen kann. Solche Einschränkungen würde ich lieber vermeiden.
Damit meine ich, dass du für jede zusätzliche Fischart eine neue Klasse programmieren müsstest, was nicht nötig wäre, wenn du nur eine Fischklasse hättest, die du entsprechend konfigurierst. Das hätte auch den Vorteil, dass dein Code solange universell bleibt, bis du an Grenzen kommst. Dann kannst du immer noch die Klassen einführen, die du zur Problemlösung wirklich brauchst und hast auch automatisch eine Begründung dafür (bisher sind das ja eher emotionale als technische Begründungen gewesen). Es ist nicht sinnvoll, Lösungen einzubauen, für die man das passende Problem noch nicht kennt.
Inwiefern verbaust du dir denn etwas, wenn du eine Klasse erst dann einführst, wenn du eine konkrete Begründung dafür hast? Solange du noch keine Vorstellung davon hast, was das für verrückte Sachen sein sollen, kannst du doch auch nicht wissen, wie die geeignete Klassenhierarchie aussieht. Vielleicht ist das ja wirklich eine, die eine Klasse pro Fischart erfordert, aber aus den bisherigen Informationen lässt sich das meines Erachtens noch nicht ableiten.
Unter'm Strich wäre es eben sinnvoller, wenn du zunächst festlegst, was in deinem Spiel eigentlich passieren soll und dann die passenden Klassen festlegst. Wenn die Klassenhierarchie für dich sowieso schon fest steht, ist die Frage nach einer Empfehlung eigentlich auch sinnlos, denn du bist offenbar auf der Suche nach einer Aufgabenstellung, die zu der von dir gewünschen Klassenhierarchie passt.
Ja ne da habt ihr natürlich recht^^ Ich bestehe nur deshalb so sehr auf die Fischtypen-Klassen, da es ja immer noch ein Uni-Projekt ist und ich so Dinge wie Interfaces, abstrakte Klassen und erbende Klassen drin haben möchte bzw. auch muss. Deshalb pass ich das Spiel halt etwas daran an Aber ist ok, ich bin zufrieden damit wie es gerade ist. Danke euch!