Designprobleme: Vererbungen vs. statisch

Haave

Top Contributor
Hallo,

ich arbeite momentan an einem kleinen Spiel (geht in die Richtung "Harvest Moon", eine Farmsimulation, falls das jemandem etwas sagt) und stehe hier vor zwei Designproblemen.

1.) Ich habe eine Superklasse Item, unter der alle Klassen zusammengefasst sind, mit denen man Kauf- und Verkaufaktionen durchführen kann (z.B. Kartoffel oder Huhn). Jede Subklasse hat einen ihr eigenen Einkaufs- und Verkaufspreis. Von der Superklasse erben sie get-Methoden, damit man auf diese Werte zugreifen kann. Mir erschien es logisch, die Variablen für Einkaufs-/Verkaufspreis sowie die get-Methoden als static zu markieren, weil ich ja z.B. den Kaufpreis einer Kartoffel auch dann erfahren können möchte, wenn keine Instanz von Kartoffel existiert. Das führt aber dazu, dass ich diese Methoden nicht mehr dynamisch aufrufen kann, z.B. bei meiner Methode buyItem():
Java:
public void buyItem(Item i) {
	int priceOfItem = i.getPriceWhenBought();
	if(priceOfItem < money) { //money ist das Budget des Helden
		money = money-priceOfItem;
		/*
		 * Hier soll das zum Parameter i zugehörige Objekt erzeugt werden.
		 */
	}
}
Gebe ich hier eine Kartoffel rein, soll mit i.getPriceWhenBought der Preis der Kartoffel erfragt werden. Mache ich es aber statisch, müsste ich Item.getPriceWhenBought() schreiben und dann funktioniert das ganze nicht mehr. Ich bräuchte also mindestens eine Instanz einer Kartoffel, die ich immer nach ihrem Preis fragen kann, eine "Samplekartoffel", die im Laden rumliegt und nie verkauft wird. Das scheint mir aber kein besonders guter Stil zu sein.

2.) Das zweite Problem knüpft an das erste an: Wenn die Kartoffel nun gekauft wird, soll eine neue Instanz von Kartoffel erzeugt werden. Dies soll ebenfalls dynamisch geschehen: Wenn ich in buyItem(Item i) statt der Kartoffel ein Huhn reingebe, soll der Preis des Huhns nachgeschaut und ein Huhn erzeugt werden. Wie aber gestalte ich das?


Ich hoffe, dass ich meine Schwierigkeiten verständlich erklärt habe und mir jemand helfen kann.

Grüße
Haave
 

Empire Phoenix

Top Contributor
Hm also direkt das Problem könnte man über Reflection lösen, aber das sollte nicht der weg hierfür sein, da es das designproblem nur umgeht.

Möglichkeit A: jedes Item bekommt eine Descriptor Klasse, auf die statisch zugegriffen werden kann und wo alles drinne steht ausser der logik ( bild preis name ect, aber nciht wachstumsfunktion)

Möglichkeit B: Hashtable, erstelle eine tabelle, die Itemname auf Preise mappt.

Möglichkeit C: habe den wert statisch, aber erstelle ebenfalls einen getter der nicht statisch ist und den statischen wert zurückgibt.

Am einfachsten ist sicherlich die C aber designtechnisch auch nicht gerade intelligent.
B ist ein relativ sauberer Kompromiss, statt strings kann zb auch das (statisch)Kartoffel.class bzw (bei instanz)i.getClass() genommen werden.
A ist im generellen wenn sowieso einen Datenbank benutzt wird zum speichern des Spielstandes / Spielstände(multiplayer?) ganz sinnvoll, weil man dann die Werte seperat von der logik 1 zu 1 (naja fast) in tabellen mappen kann.
 

XHelp

Top Contributor
1.:
Ich würde eher zu B von Empire Phoenix tendieren. Der Klasse Huhn soll es ja egal sein, wie teuer die ist. Du kannst ja einen "PriceC-alculator"-Klasse schreiben, die dir die Preise für alles rausspruckt.
Bei Möglichkeit C sehe ich auf anhieb nicht, in wie fern das zur Lösung beitragen könnte
2.: Hier wäre Factory das richtige Stichwort. Du machst eine Klasse, die dir Items zurückgibt. Du rufst
Code:
make("huhn")
und sie gibt dir eine Instanz von Huhn zurück.
 

bs

Mitglied
Warum ist es der Klasse Huhn egal wie teuer sie ist? Eine Klasse soll doch immer alle ihre Eigenschaften kennen? Sonst macht Objektorientierung irgendwie wenig Sinn..

Also in unserem Kurs über objektorientiertes Design wird so etwas eindeutig so gelöst: für jedes Produkt gibt es eine ItemDescription, die sich Name, Beschreibung, Preis,... also alles was bei jedem dieser Produkte gleich ist, hält. Jedes Item hat eine Referenz auf die zugehörige ItemDescription. Du kannst dann trotzdem eine Superklasse für Item machen, die alle deine Methoden implementiert. (zB huhn.getDescription().getPrice()).
 
Zuletzt bearbeitet:

XHelp

Top Contributor
In eurem Kurs ist ja dem Item auch egal, wie teuer es ist, da es nicht in der Klasse gespeichert ist, sondern woanders. ;)
Wenn du eine Referenz auf die Preise IN der Item-Klasse hast, dann brauchst du ja auch entweder eine Instanz davon um an die Referenz ranzukommen oder die Referenz ist statisch. Würde auch nichts am bestehendem Problem lösen.
Meiner Meinung nach würde sich hier einiges leichter bewerkstelligen lassen, wenn man ein Paar Informationen auslagern würde
 

bs

Mitglied
Kommt drauf an was "egal" heißt +g+ also zumindest kennt es seine ItemDescription die den Preis kennt... aber lösen tut es das Problem wirklich nicht...

Neuer Vorschlag:
ItemDescription ist eine abstrakte Klasse mit Preis, Name,... Huhn, Kartoffel usw. erben von dieser Klasse und halten sich jeweils ein statisches Attribut mit dem Preis. Die Klasse Item hält sich wie zuvor eine Referenz auf die jeweilige Subklasse der ItemDescription, der statische Preis kann dann ja auch dynamisch ausgelesen werden... Die Subklassen könnten zusätzlich auch noch eine Factory für Items sein (erzeugen also nach Bedarf mit einer statischen Funktion ein Item mit einer Referenz "auf sich selbst")..

Also Daten auslagern wird bei uns meistes als allerschlechteste Lösung angesehen ;) Aber vll ist das auch nicht immer ganz praxisnah...

Edit: Also wenn ich so darüber nachdenke ist Möglichkeit C von Empire Phoenix doch am einfachsten und besten....
 
Zuletzt bearbeitet:

XHelp

Top Contributor
Also Daten auslagern wird bei uns meistes als allerschlechteste Lösung angesehen ;) Aber vll ist das auch nicht immer ganz praxisnah...
Sich ein Arm zu brechen ist sicherlich schlecht. Aber wenn ich nur die Wahl habe zwischen "sich ein Arm brechnen" und "sich das Genick brechen", dann sieht die Sache schon ganz anders aus :)

Also mit statischen Methoden kommt man ohne Reflection nicht weiter (imho, es kann sein, dass ich etwas übersehe), mit einem Preis-getter auch nicht, da man ja eine Instanz braucht.
Deswegen kann ich nicht wirklich nachvollziehen, was die Lösung bringen soll... du kannst natürlich "null-Objekte" davon mitschleppen, die anschließend casten und die statische Methode ausführen, aber dann sind wir schnell bei "sich das Genick brechen"
 
B

bygones

Gast
ansich finde ich auch dass Preis bzw Verkaufshandlung nichts in dem Itemobjekt zu suchen hat. Die Frage hier ist nach dem gesamtdesign. Ist ein Huhn nur ein Gegenstand der verkauft wird oder hat es auch andere Eigenschaften... also Huehnereigenschaften.

Ansich gehört der Preis nicht ins Huhn - vor allem was passiert mit einem Huhn wenn es verkauft wurde ? existiert es nicht mehr ?

Es sollte eine andere struktur geben die verwaltet was welches Item wo wieviel kostet und was passiert wenn eins verkauft bzw gekauft wurde (oder soll das Huhn einen zähler haben wie oft es gehandlet wurde ??... nein)

Es handelt sich hierbei um zwei klare logische unterschiedliche Strukturen, die sollte man nicht mischen

und MEIN GOTT:.. hört bitte auf jedes Problem "mit reflection" lösen zu wollen... mitm Vorschlaghammer will man auch nicht den Nagel in ein Brett hauen....
 

Haave

Top Contributor
Vielen Dank erst mal für die anregenden Antworten!

Okay, zum ersten Problem:
Den Preis der Items in eine eigene Klasse auszulagern, ist hier vielleicht tatsächlich ganz sinnvoll. Um bygones' Frage nämlich zu beantworten: Meine Hühner und Kühe haben tatsächlich noch mehr Eigenschaften und sind nicht bloße Ein- und Verkaufsgegenstände. Sie wachsen, werden älter, können hungrig oder krank sein (oder beides, wenn der Spieler mies spielt und sich nicht kümmert :D).
Nach genauerem Nachdenken ergibt es auch irgendwie Sinn, dass ein Huhn weiß, ob es Hunger hat, aber nicht, wie viel es im Tante-Emma-Laden auf'm Dörfle kostet. Das ist ja eher Sache einer Preistabelle, die in einem Laden aushängt, und diese Aufgabe könnte ja eine eigene Klasse übernehmen.
Das werd ich mal versuchen :)


Mit der zweiten Frage bin ich trotz XHelps Post immer noch ziemlich überfordert:
2.: Hier wäre Factory das richtige Stichwort. Du machst eine Klasse, die dir Items zurückgibt. Du rufst
Code:
make("huhn")
und sie gibt dir eine Instanz von Huhn zurück.
Ich hab da gerade mal was geschrieben, aber das kommt mir nicht gerade toll vor…
Java:
public class Factory {
	public static Item makeInstance(String nameOfClass) {
		if(nameOfClass.equals("Chicken")) {
			return new Chicken();
		}
		if(nameOfClass.equals("Cow")) {
			return new Cow();
		}
		//more
		
		return new Item(); //was soll dann damit passieren?
	}
}
Probleme:
- Klasse Item kann nicht abstrakt sein (hatte sie so aber eigentlich angedacht)
- ewig viele if-Abfragen für die ganzen Farmtiere/Gemüsesamen etc., und sollte ich mich später entscheiden, dem Spiel z.B. Broccoli und Schweine hinzuzufügen, muss ich dieses Konstrukt umständlich erweitern.

Gibt's nicht eine einfachere und sauberere Möglichkeit, festzuhalten, welche Items es alles gibt, so wie eine Liste, in die ich bloß Broccoli reinschreiben brauche und schon wissen alle Methoden, die das benötigen, Bescheid?
 

Murray

Top Contributor
Spricht etwas gegen diese Lösung?
Java:
public static Item makeInstance(final String nameOfClass) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
   final class itemClass = Class.forName( nameOfClass);
   if ( !Item.class.isAssignableFrom( itemClass) throw new IllegalArgumentException( "expected Item, got " + itemClass.getName());
   return (Item)(itemClass.newInstance());
}
 
M

maki

Gast
Gibt es einen Grund auf Strings (oder gar Reflection) zurückzugreifen?
Warum nicht spezialisierte Factory Methoden mit aussagekräftigen Namen?
 

Murray

Top Contributor
Ich habe mich nur auf Haaves Post von 21:17 bezogen - das dort beschriebene Problem könnte man durch kosequenten Einsatz von Reflection lösen, ohen das API verändern zu müssen.

Aber natürlich wäre es besser, statt
Java:
make( "XYZ")
etwas wie
Java:
makeXYZ()
zu verwenden, wenn die "XYZ" im Code nur als Literale auftauchen und nicht irgendwie dynamisch zustande kommen.
 

Marco13

Top Contributor
Ja, so war das wohl ursprünglich auch mal gemeint
Java:
interface ItemFactory
{ 
    public Item create();
}
class HuhnFactory implements ItemFactory // Hier müßte noch ein "extends Huhn" hin *grins*
{
    public Item create() { return new Huhn(); }
}

Ob die Instanzen denn nun tatsächlich in dem Moment erzeugt werden sollen, in dem man sie kauft ... joa, kann man sich überlegen ... in bezug auf die "Realität" ist das ja Unfug, aber irgendwo müssen die ja herkommen. Wenn man irgendwann mal modellieren will, dass etwas ausverkauft ist, merkt man, dass das nicht passt.
Der Preis... hm. Man müßte wissen, was genau da am Ende rauskommen soll. Aber wenn in zwei verschiedenen Lädern die Dinge zu unterschiedlichen Preisen verkauft werden sollen, ist klar, dass der Preis keine "inheränte" Eigenschaft der Objekte ist (vielleicht der Objekte - auch DAS müßte man sich überlegen - aber dann mit Sicherheit nicht der Klassen)
 
M

maki

Gast
Ob die Instanzen denn nun tatsächlich in dem Moment erzeugt werden sollen, in dem man sie kauft ... joa, kann man sich überlegen ... in bezug auf die "Realität" ist das ja Unfug, aber irgendwo müssen die ja herkommen. Wenn man irgendwann mal modellieren will, dass etwas ausverkauft ist, merkt man, dass das nicht passt.
Das ist ja das schöne an einer Factory, sowas kann man da schön kapseln, mit einem new geht das nicht mehr... ob wirklich jedesmal ein neues Objekt erzeugt wird oder nur ein & dasselbe zurückgibt ist Sache der Factory. Wenn etwas ausverkauft sein könnte, kann man ja eine Methode haben die einem vorher sagt ob es Sinn macht ein Huhn zu bestellen ;) Kompliziert wird es bei der Nebenläufigkeit... aber mit Synchronizsation und Exceptionhandling kann man das auch noch hinbiegen ("OutOfStockException").
 
Zuletzt bearbeitet von einem Moderator:

Marco13

Top Contributor
Hm. Ob die "Intelligenz", zu entscheiden, ob ein Laden noch Güter hat, in der Factory liegen sollte? Diese Factory passt SO wenig zu irgendwas "realem", dass sie IMHO bestenfalls gut versteckt irgendwo ihren Dienst tun sollte. Aber wie man sie dann geeignet in das Gesamtkonzept integriert, ist eine weitere (offene) Frage...
 
M

maki

Gast
Hm. Ob die "Intelligenz", zu entscheiden, ob ein Laden noch Güter hat, in der Factory liegen sollte? Diese Factory passt SO wenig zu irgendwas "realem", dass sie IMHO bestenfalls gut versteckt irgendwo ihren Dienst tun sollte. Aber wie man sie dann geeignet in das Gesamtkonzept integriert, ist eine weitere (offene) Frage...
Wir dürfen uns gar nicht immer an der realen Welt orientieren, deswegen heisst es ja Abstraktion & Modellierung, aber ich sehe ein das die Entscheidung ob es noch Hühner gibt nicht zwingend in der Factory implementiert werden muss.
 

Haave

Top Contributor
@Murray:
Keine Ahnung, ob etwas gegen oder für diese Lösung spricht; ich hatte mit Reflections noch nie zu tun… :oops:

Ich erläutere das Spielprinzip mal etwas näher:
Für den Anfang möchte ich es möglichst simpel halten, d.h., es gibt (vorerst) keine konkurrierenden Läden mit Preisunterschieden bei den angebotenen Waren und keine Warenknappheiten, und die benötigten Objekte werden erst dann erzeugt, wenn der Spieler sie benötigt - bei Tieren ist das natürlich nicht wirklich realistisch, aber es wird eine spielinterne Uhr geben, die Zeit und Datum misst, damit das Geburtsdatum des gekauften Tieres entsprechend auf einen sinnvollen Zeitpunkt gesetzt werden kann.

Java:
interface ItemFactory
{ 
    public Item create();
}
class HuhnFactory implements ItemFactory // Hier müßte noch ein "extends Huhn" hin *grins*
{
    public Item create() { return new Huhn(); }
}
Eine solche Lösung würde aber bedeuten, dass ich - wie oben schon geschrieben - für jedes neu hinzkommende Item wieder eine extra Klasse und extra Code schreiben muss. Geht es nicht dynamischer? Oder ist der von Murray gepostete Code vielleicht schon das dynamischste, was ich kriegen kann?
 

Ark

Top Contributor
Mir gefällt ja spontan der Ansatz mit der "Samplekartoffel" am besten. Jeder Laden, der Kartoffeln verkauft, hat eine eigene "Samplekartoffel", deren Preis jeder Laden unabhängig von anderen Läden festlegen kann. Wenn man dann Kartoffeln kaufen möchte, wird natürlich nicht die Samplekartoffel hergegeben, sondern aus ihr geklonte Kartoffeln.

Wenn die Kartoffeln überall gleich viel kosten sollen, führt einfach nicht jeder Laden eine eigene Samplekartoffel, sondern bezieht sich auf eine ladenübergreifende Samplekartoffel.

Spricht irgendetwas gegen diesen Ansatz?

Ark
 
B

bygones

Gast
@Murray:
Keine Ahnung, ob etwas gegen oder für diese Lösung spricht; ich hatte mit Reflections noch nie zu tun… :oops:
brauchst du auch nicht und solltest du auch hier nicht machen

Eine solche Lösung würde aber bedeuten, dass ich - wie oben schon geschrieben - für jedes neu hinzkommende Item wieder eine extra Klasse und extra Code schreiben muss. Geht es nicht dynamischer? Oder ist der von Murray gepostete Code vielleicht schon das dynamischste, was ich kriegen kann?
bzgl der Factory ?
da bräuchtest du keine neuen Klassen - neuen code natürlich
Java:
class ItemFactory
{
    public Item createHuhn() { return new Huhn(); }
    public Item createKartoffel() { return new Kartoffel(); }
    // neue item -> neue create
}
dynamischer geht es immer, aber nur weil etwas geht - sollte man es nicht immer machen...

@Ark:

ich fände es besser, dass wenn ein Laden instanziiert wird, dieser die Preistabelle mitbekommt. D.h. man reicht von aussen rein was der Grundpreis einer Kartoffel bzw eines Huhn ists.
Dann braucht der Laden selbst keine Referenzen auf Items und kann sogar selbst entscheiden ob er beim Preis n Discount gibt oder aufgrund seiner Qualität mehr verlangt.
Ein Sampleitem würde ich nicht in den Laden "legen"
 

Marco13

Top Contributor
bzgl der Factory ?
da bräuchtest du keine neuen Klassen - neuen code natürlich
Java:
class ItemFactory
{
    public Item createHuhn() { return new Huhn(); }
    public Item createKartoffel() { return new Kartoffel(); }
    // neue item -> neue create
}
???:L Ähm. Sonst ist das, was du schreibst, doch nicht sooo unvernünftig, aber DAS ist doch ... genau das, was man mit einer Factory normalerweise gerade vermeiden will (ob die Factory hier als Lösung geeignet ist, mal außen vor gelassen) : Die Grundidee ist doch, dass es EINE abstrakte Klasse mit EINER create()-Methode gibt, und die Wahl, was dort erzeugt wird, der konkreten Implementierung der Factory überlassen bleibt. Was würden denn die Factory bringen, wenn man zum Aufrufen der richtigen Methode DOCH wieder [c]if (name.equals("huhn")) fac.createHuhn(); else if...[/c] machen muss?

Aber nochmal allgemein: Wenn man die Anforderungen (und die Zielsetzung) nicht kennt, kann man da kaum die geeignetste Lösung finden. Fragen, die sich sofort stellen:
- Sollen Waren ausverkauft sein können?
- Soll ein Lagerbestand abgefragt werden können?
- Wovon soll der Preis abhängen?
--- Z.B. Angebot und Nachfrage, Lagerbestand...
- Soll es unterschiedliche Preise in unterschiedlichen Läden geben?
- Soll der Preis nur von der Produktklasse abhängen, oder von der Instanz?
--- Z.B. Kartoffel mit gewicht=100 kostet 1€, mit gewicht=200 kostet 3€
- Und noch viel viel mehr...
 

slawaweis

Bekanntes Mitglied
mit Objektorientierung kann man auch übertreiben. Ich habe auch früher versuche jede Kleinigkeit als Objekt zu modellieren, bis irgendwann die Programme unwartbar geworden sind. Objekte braucht man, wenn man mit diesen direkt arbeitet. In einem Spiel spielt man mit Daten, nicht mit Objekten. Mein Vorschlag wäre ein dynamischer.

(1) Für alle Konstanten im System wird eine eigene Quelle erstellt, was eine TXT-, CSV-, XML-Datei oder eine Datenbank sein kann. Es werden keine Spieldaten im Quelltext abgelegt. Mit verschiedenen Datenbeständen kann man sogar verschiedene Ausgangssituationen im Spieles realisieren.

(2) Der nächste Schritt wäre eine Abstraktion, welche den Zugriff auf diese Daten bietet:
Java:
public interface IMarket
{
 public int getPrice(String item_name);
 public int getCount(String item_name);
}
auf diese Weise könne man sogar saisonale Schwankungen realisieren.

(3) Abstraktion eines Items:
Java:
public interface IItem
{
 public Object getProperty(String name);
 public void setProperty(String name, Object value);
}
Items werden so dynamisch, können beliebige Eigenschaften aufnehmen und es muss nicht für jedes Item eine eigene Klasse definiert werden. Als Unterscheidung kann einfach folgendes gemacht werden:

Java:
if(item.getProperty("type").equals("huhn"))
  {
  // ...
  }

(4) Die Konstruktion der Inhalte der Items sollte wieder über eine externe XML-Datei/Tabelle erfolgen. Alternativ kann man so eine Factory realisieren:

Java:
public interface IItemFactory
{
 public IItem createItem(String name);
}

protected class ItemFactoryImpl implements IItemFactory
{
 protected Map<String name, IItemFactoryProvider provider> map = ...;

 public IItem createItem(String name)
  {
  return (map.get(name) != null ? map.get(name).create(); : null);
  }

 public void add(String name, IItemFactoryProvider provider)
  {
  return map.add(name, provider);
  }
}

protected interface IItemFactoryProvider
{
 public IItem create();
}

Hinzufügen von konkreten "Item Providern", wobei "ItemImpl" eine einfache Map implementiert.

Java:
ItemFactoryImpl factory = new ItemFactoryImpl();

factory.add("huhn", new IItemFactoryProvider() { public IItem create() {
  return ItemImpl("type", "huhn",
                  "price", 20,
                  "age", 1,
                  "weight", 5
                 );
}});

Slawa
 
Zuletzt bearbeitet:
B

bygones

Gast
???:L Ähm. Sonst ist das, was du schreibst, doch nicht sooo unvernünftig, aber DAS ist doch ... genau das, was man mit einer Factory normalerweise gerade vermeiden will (ob die Factory hier als Lösung geeignet ist, mal außen vor gelassen) : Die Grundidee ist doch, dass es EINE abstrakte Klasse mit EINER create()-Methode gibt, und die Wahl, was dort erzeugt wird, der konkreten Implementierung der Factory überlassen bleibt. Was würden denn die Factory bringen, wenn man zum Aufrufen der richtigen Methode DOCH wieder [c]if (name.equals("huhn")) fac.createHuhn(); else if...[/c] machen muss?
wie du sagtst - wenn man die Anforderung nicht kennt ists schwer die korrekte Lösung zu finden.
Meine Factory ist eine valide wenn der aufrufende code explizit sagen kann "Hey Factory, gib mir mal bitte n Huhn". Wenn der Aufrufe diese Entscheidung aber über krude und krumme ecken tun muss stellt sich das natürlich in Frage.

@slawa:
argg.. ich hasse diese [c]I[/c] vor den Interfaces... stoert das lesen.
und wie du sagst OOP kann man übertreiben, so auch das "dynamsisch"... ein Item mit Eigenschaften zu füttern wie man grad will ist mehr als ungelenkig

PS:
Ich vermute mal wir haben so und so den TE schon abgehängt ;-)
 

Marco13

Top Contributor
(3) Abstraktion eines Items:
Java:
public interface IItem
{
 public Object getProperty(String name);
 public void setProperty(String name, Object value);
}
Items werden so dynamisch, können beliebige Eigenschaften aufnehmen und es muss nicht für jedes Item eine eigene Klasse definiert werden. Als Unterscheidung kann einfach folgendes gemacht werden:

Java:
if(item.getProperty("type").equals("huhn"))
  {
  // ...
  }

Ich kenne die Problematik, den Wunsch "so etwas" zu machen (im Hinblick auf die einfache Möglichkeit, neue Properties hinzuzufügen), und die theoretische Möglichkeit der Lösung, die du angedeutet hast.

Was mir dabei Bauchschmerzen verursacht ist folgende Frage: Warum solle man nicht sowas definieren wie
Java:
class Thing extends Object
{
    private Map<Object, Object> map = new HashMap<Object, Object>();

    public Object get(Object key) { return map.get(key); }
    public void set(Object key, Object value) { map.put(key, value); }

    abstract Object execute(Object action, Object ... parameters);
}
und diese Klasse als Oberklasse für alles verwenden? Jede Objektorientierte Struktur ließe sich mit diesen 3 Methoden vollständig beschreiben. Statt
Code:
print(huhn.getWeight());
huhn.friss(new Korn());
print(huhn.getWeight());
schreibt man dann halt
Code:
print(huhn.get("Weight"));
huhn.execute("friss", new Korn()));
print(huhn.get("Weight"));

Klingt aber irgendwie suspekt ... :reflect: ;)
 

slawaweis

Bekanntes Mitglied
Klingt aber irgendwie suspekt ... :reflect: ;)
ich kenne das Gefühl. Ich beschäftige mich schon seit ein paar Jahren mit dieser Art des Designs und das größte Problem dabei ist, dass es einfach kein Buch gibt, welches diese Art gut erklärt, mit Vor- und Nachteilen. Die klassischen objektorientierten Lehrbücher versucht es oft den Anfänger mit solchen Beispielen wie: "ein Auto ist ein Objekt" - zu erklären. Doch letztendlich ist ein richtiges Programm wie eine Stadt. Es gibt viele verschiedene Autos, klein und groß; es gibt eine Wegführung; es gibt aktiven Verkehr, der über Ampeln und Schilder geleitet wird; es gibt Unfälle, die so schnell wie möglich gelöst werden müssen. Genau diese größere Sicht kommt in den meisten Büchern zu kurz. Programme werden ja auch geschrieben, um ein System zu erstellen und nicht nur einzelne Autos. Wenn man sich über längere Zeit mit der größere Sicht beschäftigt, fragt man sich, ob man überhaupt noch so viel Mühe in die einzelnen Autos steckt, da ja sowieso jede Woche neue Exemplare (neue Funktionalität) dazu kommen.

Um zurück zum Thema zu kommen, man könnte tatsächlich ein Objekt "Thing" erstellen und damit alles machen. Doch es gibt noch die semantische Sicht. Mit der Strukturierung über Klassen kann man die Logik wiederspiegeln. Es gibt eben ein Item, was beliebig viele Eigenschaften hat. Dann gibt es Factories, welche bestimmte Items nach bestimmten Regeln produzieren. Dann gibt es so was wie ein Steuerzentrale, welche die Koordinierung übernimmt. Dazu gibt es noch eine Datenbank, welche die Werte im System verwaltet. Das wäre die semantische Sicht und das könnte man über Klassen ausdrücken. Wie grob oder fein man das macht, ist jedem selber überlassen.

Man kann es auch aus anderer Sicht betrachten. Ein System, welches für jedes Item eine eigene Klasse benutzt, muss für jedes neues Item neukompiliert werden. Ein System, welches die Items aus einer Textdatei oder Datenbank lädt, kann während der Laufzeit verändert werden. Das Item-Design kann sogar von anderen Menschen im Team übernommen werden, die nicht programmieren können, aber die Tabellen mit Werten zusammenstellen und testen. Arbeitsteilung eben.

Slawa
 

Empire Phoenix

Top Contributor
hat aber einen großen anchteil, sowas geht nur bei gleichen/ähnlichen objecten gut, je mehr individuelle logic du hast desto mehr komplexität. (Weil die logic für alles am anfang beschreiben sein muss im hauptentity in das die werte reingeleaden werden. Viele oo vorteile gehen dann wieder verloren.) hängt natürlich vom bedarf ab.
 

Jay_030

Aktives Mitglied
Die Idee von slawaweis ist gar nicht mal so schlecht, wobei man das vielleicht etwas anders gestalten sollte. Jedes Item hat bestimmte Basis-Informationen wie z.B. einen Mindestpreis, eine Bezeichnung etc. pp, die normal im Code beschrieben sind, und eine endliche Menge an dynamisch beschreibbaren Zusatzinformationen, die man über XML oder eine Datenbank pflegt. Denke, wenn man es so gestaltet, ist der Ansatz von slawaweis legitim. Denn für 100 unterschiedliche Items, 100 Klassen mit stupiden Gettern und Settern... ist auch nicht gerade förderlich. Es sei denn, man benötigt noch zusätzliche Logik. Diese Herangehensweise hält einem aber nicht davon ab, für bestimmte Item-Typen weitere Klassen zu erstellen.
 

Marco13

Top Contributor
Grundsätzlich wäre das ja eher die Richtung, die man üblicherweise geht. Wenn JEDES Item IMMER einen Preis hat, wäre es ja albern, den dann in so einer Map zu speichern.
Bei diesen allgemeinen "Properties" (und was anderes wäre das dann ja eigentlich nicht) stellt sich immer die Frage, WER auf diese Informationen WANN und WIE zugreift. Jeder, der weiß, dass er darauf zugreifen kann, könnte diese Information ggf. auch (compilezeit-überprüft) über ein Interface bekommen (sofern es sowas wie Duck-Typing nicht gibt). Zumindest die Gefahr ist offensichtlich: Ich hatte und habe mit Systemen zu tun, wo in den absurdesten Klassen irgendwelche unmotivierten HashMaps liegen, in denen "irgendwelche" Properties gespeichert sind, die kreuz und quer von jedem gelesen und geschrieben werden, und keiner weiß, was dort wann drin steht :autsch: Ich finde, man kann seinen Arbeitsplatz auch anders sichern.
 

Jay_030

Aktives Mitglied
Ich habe so etwas ähnliches mal gemacht. Es sollten Räume verwaltet werden, deren Eigenschaften erst zur Laufzeit durch den User definiert werden sollten. Dazu haben wir eine Klasse RoomAttribute verwenden, die generisch typisiert wurde und über ihren konkreten Werte-Typ (String, Int, Boolean, ...) Auskunft geben konnte. Davon ausgehend konnten wir sehr einfach eine sich dynamisch aufbauende Oberfläche zur Anzeige und Eingabe der Raumeigenschaften erstellen (für String ein Textfeld, für Int ein Textfeld mit Eingabeüberprüfung usw). Daneben hatte jeder Raum natürlich Basis-Eigenschaften, die von vornherein klar waren (z.B. Größe, Anzahl Plätze). Das hat sich sehr gut bewährt und wir hatten Typüberprüfung zur Laufzeit. :)

So kann man doch durchaus auch hier verfahren. Jetzt mal abgesehen, welche Eigenschaften jedes Item hat. Das wird erst aus den Anforderungen deutlich.
 

Haave

Top Contributor
Uff, ihr überfordert mich xD

Naja, aber ihr habt mich auch ins Nachdenken gebracht und ich merke, dass ich mir über viele Details sowie die letztendliche Struktur meines Spiels noch nicht hinreichend klargeworden bin. (Hab halt erst mal drauf losgecodet, weil ich mich mit so einem trockenen Project Spec - trotz des Vorhabens, eins zu machen - nicht so richtig anfreunden konnte ^^' … tja, und schneller, als man denkt, rennt man dann gegen die Wand, und gegen die nächste, und die nächste, weil einfach die Orientierung fehlt…)

Aber der Vorschlag von Jay_030 hat mein Interesse geweckt, da meine Farmtiere ebenfalls bleibende sowie durch den Spieler veränderbare Eigenschaften besitzen (ersteres z.B. Alter, das läuft einfach immer weiter, letzteres z.B. Name eines Huhns). Kannst du (@Jay_030) das vielleicht noch genauer erklären?
 

DrZoidberg

Top Contributor
Also den Programmcode und die Daten zu trennen ist grundsätzlich eine gute Idee. Das wird bei allen professionellen Spielen so gemacht.

Vielleicht ginge sowas?

Java:
interface Item {
    public String getName();
    public int getPrice();
}

class ItemFactory {
    final private String name;
    final private int price;
    
    ItemFactory(Map<String, String> properties) {
        name=properties.get("name");
        price=Integer.parseInt(properties.get("price"));
    }

    public String getName() {
        return name;
    }
    public int getPrice() {
        return price;
    }

    public Item newInstance() {
        return new Item() {
            public String getName() {
                return name;
            }
            public int getPrice() {
                return price;
            }
        };
    } 
}

interface DataFile {
    public Map<String, String> loadNextData();
    public boolean hasMoreEntries();
}

class ItemDataFile implements DataFile {
    final private File file;
    
    public ItemDataFile(String fileName) {
        file=new File(fileName);
    }
    public Map<String, String> loadNextData() {
        return null;
    }
    public boolean hasMoreEntries() {
        return true;
    }
    
    public ArrayList<ItemFactory> loadAllItems() {
        ArrayList<ItemFactory> list=new ArrayList<ItemFactory>();
        while(hasMoreEntries()) {
            ItemFactory factory=new ItemFactory(loadNextData());
            list.add(factory);
        }
        return list;
    }
}
 
Zuletzt bearbeitet:

Ruzmanz

Top Contributor
Mir gefällt ja spontan der Ansatz mit der "Samplekartoffel" am besten. Jeder Laden, der Kartoffeln verkauft, hat eine eigene "Samplekartoffel", deren Preis jeder Laden unabhängig von anderen Läden festlegen kann. Wenn man dann Kartoffeln kaufen möchte, wird natürlich nicht die Samplekartoffel hergegeben, sondern aus ihr geklonte Kartoffeln.

Wenn die Kartoffeln überall gleich viel kosten sollen, führt einfach nicht jeder Laden eine eigene Samplekartoffel, sondern bezieht sich auf eine ladenübergreifende Samplekartoffel.

Spricht irgendetwas gegen diesen Ansatz?

Ark

Ich wollte mal auf diesen Ansatz verweisen. Mich würde auch interessieren, ob das nicht besser wäre. Zumindest habe ich mein Spiel so realisiert. Es ist verbraucht zwar "mehr" Speicher, aber bei 2000 neuen Kartoffeln in der Stunde macht das am Ende keinen unterschied mehr, ob nun eine Klasse zusätzlich vorhanden ist. Sollte mal ein Spieler ein gewisses Vermögen aufgebaut haben, dann wird irgendwann dieser Fall eintreten. Wozu das Leben schwer machen, wenn immer die gleiche Kartoffel/Huhn angefordert wird mit den selben Eigenschaften?
 

Jay_030

Aktives Mitglied
Aber der Vorschlag von Jay_030 hat mein Interesse geweckt, da meine Farmtiere ebenfalls bleibende sowie durch den Spieler veränderbare Eigenschaften besitzen (ersteres z.B. Alter, das läuft einfach immer weiter, letzteres z.B. Name eines Huhns). Kannst du (@Jay_030) das vielleicht noch genauer erklären?
Hups, habe dich fast vergessen. :eek:

Wirklich beeindruckend ist die Sache nicht. Du kapselst einfach eine Eigenschaft, die ansonsten pur in Item wäre, in einem weiteren Objekt z.B. ItemAttribute. Über ItemAttribute könntest du dann z.B. festlegen, welche Typen erlaubt sind (z.B. nur String, Integer und Boolean).

Aber am besten wäre es, wie du schon selbst erkannt hast, erstmal genau deine Anforderungen zu formulieren.

Und zu Punkt 2 aus deinem Eröffnungspost: Das müsste durch normalen Polymorphimus funktionieren, insofern die unterschiedlichen Item-Klassen über den gleichen Konstruktor instanziert werden können.
 

JanHH

Top Contributor
Ich schreib jetzt mal was altkluges (hab auch schon Rotwein getrunken, da darf ich das).. irgendwie gehts bei solchen Fragestellungen immer darum, einen softwaredesignmässig sinnvollen Entwurf zu finden, und für einen solchen ist es notwendig, die Aufgabenstellung richtig zu verstehen und richtig zu durchdenken und daraus ein sinnvolles Objekt-Modell zu entwerfen. Wenn man das einmal geschafft hat, sind solche Fragen wie die, um dies hier geht, quasi hinfällig, es ergibt sich aus dem Modell alles automatisch.
 

Jay_030

Aktives Mitglied
Ich schreib jetzt mal was altkluges (hab auch schon Rotwein getrunken, da darf ich das) [...]
Nachtarbeit, andere Zeitzone oder schon abhängig? *feix* :-D

[...] irgendwie gehts bei solchen Fragestellungen immer darum, einen softwaredesignmässig sinnvollen Entwurf zu finden, und für einen solchen ist es notwendig, die Aufgabenstellung richtig zu verstehen und richtig zu durchdenken und daraus ein sinnvolles Objekt-Modell zu entwerfen. Wenn man das einmal geschafft hat, sind solche Fragen wie die, um dies hier geht, quasi hinfällig, es ergibt sich aus dem Modell alles automatisch.
Zu 99 % ja, aber Ausnahmen bestätigen die Regel. Bei dieser Fragestellung hier muss ich dir Recht geben. Habe das mal aus Interesse modelliert, so Pi mal Daumen zur Übung, und lediglich mit Interfaces. Es ging ganz gut. Wichtig dafür war, den Vorschlag von bygones mit der Preisliste aufzugreifen und Item von der Wirtschaftslogik zu entschlacken. Habe dann mit einem MarketActor (kann ein Shop oder auch der Held sein) und MarketTransaction (Wer mit wem, wann, wie viel zu welchem Preis gehandelt hat) gearbeitet. Aber ich weiß natürlich nicht, ob es den wirklichen Anforderungen entspricht. War ja nur nach meinem eigenen Gutdünken.

Mich würden ernsthaft die genauen Anforderungen vom Threadersteller interessieren. :)
 

Haave

Top Contributor
Mich würden ernsthaft die genauen Anforderungen vom Threadersteller interessieren. :)
:oops:
Naja, hab ja in meinem letzten Post hier geschrieben, dass mir die Anforderungen selbst nicht ganz klar sind…
Die Idee dieses Spiels ist noch sehr frisch und ich bin verhältnismäßig gesehen noch Anfänger, deswegen mag der Thread für alte Hasen überflüssig erscheinen, aber ich lerne durch ihn sehr viel :)
Ich werde mir mal genauere Gedanken machen. Hab jetzt allerdings erst mal ne Weile kein Internet, also nicht wundern, wenn ich hier eine Weile nicht reinschaue.
 

Ähnliche Java Themen

Neue Themen


Oben