Aufgabestellung mit Entwurfsmuster impl.

Status
Nicht offen für weitere Antworten.
T

ThomasD.

Gast
Frösche und Prinzessinen erforschen eine Folge von miteinander verbundenen Höhlen.
Eine Höhle kann sein:
- eine verzauberte Höhle, in der Prinzessinen eine bestimmte Anzahl von Blümen pflücken können und die zu einer anderen Höhle führt.
- ein Tümpel, in dem Frösche eine Anzahl von Fliegen fressen können und der zu einer anderen Höhle führt.
- Eine Ausgangs-Höhle, in welcher Prinzessinen die Anzahl der geflückten Blumen ausgeben und Frösche die Anzahl der gefressenen Fliegen.
- eine Kreuzungs-Höhle, die in zwei verschiedenen Höhlen führt (links und rechts). Frösche gehen immer nach links and Prinzessinen immer nach rechts.

Aufgabe:
Gebe Java Klassen (inkl. abstrakten Klassen und Interfaces) an, die obiges Verhalten implementieren, ohne irgendwelche Abfragen zu verwenden (if-else, switch etc.)
Führe dabei explizit auf, welche Entwurfsmuster verwendet wurden.

Hallo!

Ich weiß, dass ihr hier nicht meine Hausaufgaben einfach so lösen möchtet und auch sollt. Ich bin nur gerade ein bisschen verzweifelt, weil ich mit diesen Design Patterns (und auch allgemein mit Software Design) nicht gut zurecht komme.
So viel kann ich zu der Aufgabe damit leider auch gar nicht sagen...erstmal ginge es ja darum rauszufinden, welches Design Pattern man hier verwenden möchte.
"Durchgenommen" haben wir:
-MVC
-Singleton
-Fabric Method
-abstract Factory
-Adapter
-Bridge
-Composite
-Decorator
-Facade
-Flyweight
-Proxy
-Chain of Resp.,Command, Interpretor, Mediator, Memento, Observer, State, Strategy, Visitor

Allerdings springt mir davon nichts so richtig ins Auge. Vielleicht noch am ehesten die Fabric Methode/AbstractFactory weil man damit ja gut verschiedene Typen modellieren kann.
Vielleicht könnt ihr mich da erstmal an die Hand nehmen und wenigstens das richtige Entwurfsmuster (oder muss man etwa meherere kombinieren?) rausfinden.

Wäre euch super dankbar,
Thomas
 

Tobias

Top Contributor
Mh, ich erzähle jetzt mal ins Blaue hinein, denn um eine wirklich vollständige und korrekte Liste zu bekommen, muss man da sicher viel länger drüber nachdenken:

# Frosch und Prinzessing erben von einer Klasse / implementieren ein Interface Höhlenbesucher
# Die korrekte Implementierung von Höhlenbesucher muss eine Factory liefern (wobei das IMHO nicht ohne if-Entscheidung gehen wird)
# Für Höhlen gilt etwas ähnliches, die einzelnen Typen brauchen ein gemeinsames Basisinterface / -klasse und eine Factory, die die korrekte Implementierung liefert
# Höhlen sind Composites, Ausgangshöhlen sind die Blätter der Struktur, Kreuzungshöhlen die Äste
# verzauberte Höhle und Tümpel sind eventuell als Decorator zu implementieren, nämlich genau dann, wenn verzauberte Höhlen und Tümpel auch Kreuzungshöhlen sein sollen (die Aufgabenstellung ist hier nicht klar)
# Höhlenbesucher würde ich als Visitor implementieren, aber das habt ihr ja noch nicht behandelt.
# Eine Strategy für das Bewegen der Höhlenbesucher innerhalb der Höhle wäre möglich, ist aber IMHO Overkill, da jeder Höhlenbesuchertyp genau eine Strategie verfolgt.
 
T

ThomasD.

Gast
Hallo Tobias!

Vielen, vielen Dank dafür. Jetzt weiß ich wenigstens ein bisschen wie ich anfangen soll! :)
Ich habe mich auch einmal rangewagt. Erstmal habe ich mich dabei auf die Höhlen konzentriert, und versucht das mit dem Composite und der Factory hinzukriegen. Den Code findet man unten.
Für die Höhlenbesucher könnte ich durchaus den Visitor verwenden, denn den haben wir gemacht (zumindest 3-4 Folien lang)

Also, erstmal zum Composite:
- Ich wusste nicht, wie ich das geschickter machen sollte, als halt 2 Methoden addLeft/RightCave einzuführen. Geht das besser?
- normalerweise bindet man ja wohl beim Composite so eine "operation" Methode ein. Ich nehme an, die könnte ich hier irgendwie verwenden um halt das Verhalten der ganzen Geschichte zu machen? Dann müsste die Methode nur irgendwie merken, ohne if, ob jetzt ein Frosch oder eine Prinzessin in der Höhle ist.

zur Factory:
Wie kann man denn bei sowas zB die Anzahl der flowers/bugs übergeben? So wie es gerade ist, liegt die ja bei jeder Höhle bei 50.

So, das reicht erstmal.
Ist das so einigermaßen richtig, oder total daneben?

Danke euch!!

Java:
abstract class Cave {
	abstract public void operation();
    abstract public void addLeftCave(Cave aCave); 
    abstract public void addRightCave(Cave aCave);
}

class crossRoads extends Cave {
	//i am a composite
	Cave left;
	Cave right;
	public void addLeftCave(Cave aCave){
		left = aCave;
	}
	public void addRightCave(Cave aCave){
		right = aCave;
	}
	public void operation(){
		//frogs go left, princesses right
	}
}


class exitCave extends Cave {
// i am a leaf
	public void addLeftCave(Cave aCave){
	}
	public void addRightCave(Cave aCave){
	}
	public void operation(){
		//for frogs print bugs, for princesses flowers?
	}
}

class enchantedCave extends Cave {
	Cave adjacentCave;
	int flowers = 50;
	public void addLeftCave(Cave aCave){
		adjacentCave = aCave;
	}
	public void addRightCave(Cave aCave){
	}
	public void operation(){
		//princesses collect flowers
	}
}

class pond extends Cave {
	Cave adjacentCave;
	int bugs = 50;
	public void addLeftCave(Cave aCave){
		adjacentCave = aCave;
	}
	public void addRightCave(Cave aCave){
	}
	public void operation(){
		//frogs eat bugs
	}
}


//Höhlen sind Composites, Ausgangshöhlen sind die Blätter der Struktur, Kreuzungshöhlen die Äste
interface AbstractCaveFactory {
	public Cave makeCave();
}


class EnchantedCaveFactory implements AbstractCaveFactory {
	public Cave makeCave(){
		return new enchantedCave();
	}	
}

class PondCaveFactory implements AbstractCaveFactory {
	public Cave makeCave(){
		return new pond();
	}
}

class ExitCaveFactory implements AbstractCaveFactory {
	public Cave makeCave(){
		return new exitCave();
	}
}

class CrossRoadsCaveFactory implements AbstractCaveFactory {
	public Cave makeCave(){
		return new crossRoads();
	}	
}

public Cave createCave(AbstractCaveFactory factory){
	Cave aCave = factory.makeCave();
}
 

Tobias

Top Contributor
Die Methode "operation()" war bestimmt nur als Beispiel gedacht, die brauchst du hier nicht.
"addLeftCave()" und "addRightCave()" sollten nicht im Interface (das wäre besser als eine abstrakte Klasse) "Cave" definiert werden. Für Nicht-Kreuzungshöhlen kannst du diese nämlich nur leer implementieren. Außerdem hast du noch einen zweiten Asttyp in deinem Composite (nämlich verzauberte Höhlen und Tümpel, die eventuell nur in eine weitere Höhle führen).

Die konkreten Factories kennen ihre Höhlentypen, also können sie die auch entsprechend parametrisieren. Um die Schnittstelle der Factories selbst einheitlich zu halten, könntest du ein Object als Parameter zulassen, welches bei Bedarf auch null sein kann.

Die einzelnen Operationen der Höhlenbesucher (Blumen / Fliegen sammeln bzw ausgeben; durch die Höhle bewegen) werden in den Höhlenbesuchern definiert. Dadurch mußt du in der Höhle keine Fallunterscheidung machen. Siehe dazu auch den Wikipedia-Eintrag zum Visitor-Pattern.

mpG
Tobias
 
T

ThomasD.

Gast
Hallo!

Okay, wenn ich jetzt deinen Tipps folge, hätte ich doch:
Java:
interface Cave {
}
Weil ich die operation nicht brauche, und addLeft/RightCave hier auch nicht so gut aufgehoben sind. Wofür brauche ich dann überhaupt das Interface?
Was bringt es mir überhaupt, das ganze als Composite zu versuchen?
Wenn ich ich dich jetzt nämlich richtig verstanden habe, würde ich ja jetzt dieses "add"-Gedöns einfach nur in die einzelnen, speziellen Höhlen ziehen.

zu den Factories:
Das hieße ja, ich müsste den Konstruktor parametisieren. Also zB
Java:
class EnchantedCaveFactory implements AbstractCaveFactory {
    public Cave makeCave(int flowers){
        return new enchantedCave(flowers); //huch, hier sind es 'flowers' statt 50 Blumen?!
    }   
}
dann hätte ich Methoden makeCaves mit Parameter und welche ohne. Und du meinst jetzt einfach, ich könnte den Parameter einfach ins Interface aufnehmen, und dann gegebenefalls halt als "0" oder "null" übergeben?
Auch müsste die Methode "createCave" dann einen zusätzlichen Parameter haben.

Ich würde sagen, dass mit dem Visitor Pattern gehe ich erst danach an ;-)

Danke nochmal,
ThomasD.
 

Tobias

Top Contributor
Das Interface brauchst du, damit du Typsicherheit hast und auf dem Elterntyp operieren kannst. Außerdem müssen die Visitor-Empfängermethoden auch irgendwo hin ;).

makeCave() bekommt einen Parameter Object, der die Argumente für die zu erzeugenden Höhlen enthält. So hast du die maximale Freiheit bei der Parameterübergabe. Die Interfaces der Factories müssen natürlich einheitlich sein, ansonsten kann man sie ja nicht mehr austauschen. Deshalb muss der Parameter auch im Interface AbstractCaveFactory vorhanden sein.
 
T

ThomasD.

Gast
Hi!

Okay, ich habe jetzt einmal versucht umzusetzen, was du zu den Höhlen gesagt hast.
So besser? :)

Setze mich jetzt auch mal an die Besucher..

Java:
public class Object {
	Cave left;
	Cave right;
	Cave adjacentCave;
	int flowers;
	int bugs;
}

interface Cave {
}

class crossRoads implements Cave {
	//i am a composite
	Cave left;
	Cave right;
	
	public crossRoads(Object object){
		this.left = object.left;
		this.right = object.right;
	}
}


class exitCave implements Cave {
// i am a leaf
	public exitCave(Object object){
	}
}

class enchantedCave implements Cave {
	Cave adjacentCave;
	int flowers;
	public enchantedCave(Object object){
		this.adjacentCave = object.adjacentCave;
		this.flowers = object.flowers;
	}
}

class pond implements Cave {
	Cave adjacentCave;
	int bugs;
	public pond(Object object){
		this.adjacentCave = object.adjacentCave;
		this.bugs = object.bugs;
	}
}


interface AbstractCaveFactory {
	public Cave makeCave(Object object);
}


class EnchantedCaveFactory implements AbstractCaveFactory {
	public Cave makeCave(Object object){
		return new enchantedCave(object);
	}	
}

class PondCaveFactory implements AbstractCaveFactory {
	public Cave makeCave(Object object){
		return new pond(object);
	}
}

class ExitCaveFactory implements AbstractCaveFactory {
	public Cave makeCave(Object object){
		return new exitCave(object);
	}
}

class CrossRoadsCaveFactory implements AbstractCaveFactory {
	public Cave makeCave(Object object){
		return new crossRoads(object);
	}	
}

public Cave createCave(AbstractCaveFactory factory, Object object){
	Cave aCave = factory.makeCave(object);
	return aCave;
}
 
T

ThomasD.

Gast
Hallo!

Ich bins nochmal. Jetzt auch mit angefangenem Besucher.
Den kompletten bisherigen Code gibts hier:
Java:
interface caveVisitor{
	void visit(crossRoads cr);
	void visit(exitCave ec);
	void visit(enchantedCave ecC);
	void visit(Pond pond);
	void visit(Cave cave);
}

class Princess implements caveVisitor{
	int collectedFlowers;
	
	public void visit(crossRoads cr){
		//princesses go right
		this.visit(cr.right);			//KORREKT?!
	}
	public void visit(exitCave ec){
		//princesses print the number of flowers they have collected
		System.out.println("I haven collected "+collectedFlowers+"! Bye, bye");
	}
	public void visit(enchantedCave ecC){
		//picks up a fixed number of flowers
		collectedFlowers += ecC.flowers;
	}
	public void visit(Pond pond){
	}
	public void visit(Cave cave){
		System.out.println("I am in the cave now!");
	}
}

class Frog implements caveVisitor{
	int bugsEaten;
	public void visit(crossRoads cr){
		this.visit(cr.left);			//KORREKT?!
	}
	public void visit(exitCave ec){
		//frogs print out the number of bugs they have eaten
		System.out.println("I have eaten "+bugsEaten+"! Mjam, mjam!");
	}
	public void visit(enchantedCave ecC){	
	}
	public void visit(Pond pond){
		bugsEaten = pond.bugs;
	}
	public void visit(Cave cave){
		System.out.println("I am in the cave now!");
	}
}
//#########################################################################################
public class Object {
	Cave left;
	Cave right;
	Cave adjacentCave;
	int flowers;
	int bugs;
}

interface Cave {
	void acceptVisitor(caveVisitor visitor);
}

class crossRoads implements Cave {
	//i am a composite
	Cave left;
	Cave right;
	
	public crossRoads(Object object){
		this.left = object.left;
		this.right = object.right;
	}	
	public void acceptVisitor(caveVisitor visitor){
		visitor.visit(this);
	}
}


class exitCave implements Cave {
// i am a leaf
	public exitCave(Object object){
	}
	public void acceptVisitor(caveVisitor visitor){
		visitor.visit(this);
	}
}

class enchantedCave implements Cave {
	Cave adjacentCave;
	int flowers;
	public enchantedCave(Object object){
		this.adjacentCave = object.adjacentCave;
		this.flowers = object.flowers;
	}
	public void acceptVisitor(caveVisitor visitor){
		visitor.visit(this);
	}
}

class Pond implements Cave {
	Cave adjacentCave;
	int bugs;
	public Pond(Object object){
		this.adjacentCave = object.adjacentCave;
		this.bugs = object.bugs;
	}
	public void acceptVisitor(caveVisitor visitor){
		visitor.visit(this);
	}
}


interface AbstractCaveFactory {
	public Cave makeCave(Object object);
}


class EnchantedCaveFactory implements AbstractCaveFactory {
	public Cave makeCave(Object object){
		return new enchantedCave(object);
	}	
}

class PondCaveFactory implements AbstractCaveFactory {
	public Cave makeCave(Object object){
		return new Pond(object);
	}
}

class ExitCaveFactory implements AbstractCaveFactory {
	public Cave makeCave(Object object){
		return new exitCave(object);
	}
}

class CrossRoadsCaveFactory implements AbstractCaveFactory {
	public Cave makeCave(Object object){
		return new crossRoads(object);
	}	
}

public Cave createCave(AbstractCaveFactory factory, Object object){
	Cave aCave = factory.makeCave(object);
	return aCave;
}

Im Moment stellen sich mir dabei folgende Fragen:
- Wie genau mache ich das, dass Prinzessinen immer rechts und Frösche immer links gehen? So wie ich es jetzt gemacht habe? (Da steht KORREKT? dran)
- Würde ich dann noch irgendwie eine Klasse CaveSystem einführen (so wie im wiki-Bsp. "Car") was halt so ein Höhlensystem irgendwie baut? Da CaveSystem dann aber nicht wüsste, aus welchen Höhlen es genau besteht, wie leite ich den Visitor dann weiter? ..ich hoffe du verstehst was ich meine

hmm...joar...sonst fehlt halt nach die Visitor Factory.

Danke für deine bisherige Hilfe, vielleicht schaffe ich es ja doch noch bis Freitag (wobei ich "leider" Morgen den ganzen Tag in Dublin bin ;-) )

Gruß,
ThomasD.
 

Tobias

Top Contributor
Benenne niemals eine eigene Klasse wie eine Klasse aus dem JDK! Das führt nur zu Namenskonflikten! "Bekommt einen Parameter Object" heißt:

Java:
public Cave makeCave(Object params) {
    if(params == null || !(params instanceof Integer)) {
         throw new IllegalArgumentException("Integer erwartet!");
    }
    this.flowers = (Integer) params;
}

Für Kreuzungshöhlen übergibst du entsprechend ein Array von Cave (oder eine Liste). Man könnte das mit Generics eleganter lösen, aber ich vermute das sind noch böhmische Dörfer für dich. Also bleibt dir nur der direkte Cast.

Ansonsten sieht deine Lösung beim Draufgucken sehr gut aus. Einen Bug :)D) hast du noch, du setzt die gepflückten Blumen immer auf den Wert der zuletzt besuchten verzauberten Höhle. Der Operator += ist die Lösung.
 

Tobias

Top Contributor
Ich habe heute morgen einige deiner Fragen überlesen:

# Die Wegfindung ist korrekt gelöst.
# Ein CaveSystem brauchst du nicht, der Visitor wird einfach in die erste Höhle reingekippt und bewegt sich dann selbstständig fort. Dazu mußt du bei den Höhlen natürlich noch jeweils eine Folgehöhle definieren (die hast du bislang nur bei Kreuzungshöhlen) und dafür sorgen, dass der Visitor diese Folgehöhle auch besucht.

Der Bug befindet sich nicht beim Hochzählen der Blumen sondern beim Zählen der Bugs.
 
T

ThomasD.

Gast
Hi!

#Okay, Bug gefunden und beseitigt.
#
Dazu mußt du bei den Höhlen natürlich noch jeweils eine Folgehöhle definieren (die hast du bislang nur bei Kreuzungshöhlen)
Habe ich doch -> adjacentCave?
#
dafür sorgen, dass der Visitor diese Folgehöhle auch besucht.
Ok, krieg ich glaube ich hin.


zu diesem "Object params":
1. so eine makeCave methode gibts bei mir doch gar nicht? Die benutze ich doch nur, um die Konstruktoren aufzurufen?
2.Dein Beispiel ist wohl für die enchantedCave gedacht? Aber da möchte ich ja auch die "adjacentCave" setzen. Dann muss doch params irgendwie nen spezielles Object sein, weil ich einen Integer und eine Cave übergeben möchte?
 

Tobias

Top Contributor
Sorry, war gestern sehr in Eile, habe daher deinen Beitrag nicht ordentlich genug gelesen.

----

Das Schöne an der Objektorientierung ist ja, das man Elterntypen beliebig spezialisieren kann. Da java.lang.Object die Elternklasse aller Klassen in Java ist, kann ich einer makeCave(Object) beliebige Objekte als Parameter übergeben - natürlich muß die Implementierung dann entsprechend angepasst werden. Wenn du mehr als einen Parameter haben willst, übergibst du eine Map, die von der Factory definierte Schlüssel auf die richtigen Werte abbildet:

Java:
public interface AbstractCaveFactory {
    public Cave makeCave(Object object);
}
 
 
class EnchantedCaveFactory implements AbstractCaveFactory {
    public static final String ADJACENT_CAVE = "cave";
    public static final String FLOWERS = "flowers";

    public Cave makeCave(Object object){
        if(!(object instanceof Map)) {
            throw new IllegalArgumentException("Argument must be a Map, see Documentation!");
        }
        
        // Weitere Typ- und Nullprüfungen weggelassen
        Cave adjacentCave = (Cave) ((Map) object).get(ADJACENT_CAVE);
        int flowers = (Integer) ((Map) object).get(FLOWERS);
        
        return new EnchantedCave(adjacentCave, flowers);
    }   
}

Diese makeCave()-Methode ist natürlich nur als anschauliches Beispiel gemeint.

mpG
Tobias
 
T

ThomasD.

Gast
Hi again!

Folgende Sachen habe ich nun geändert/ergänzt:
- Es gibt nun eine Visitor Factory
- Visitor sollten nun auch immer in die Nachfolgehöhlen gehen.
- Ich hoffe die Object Übergabe bei der Cave-Factory ist jetzt so richtig.

Eine Frage erstmal noch: ;-)
- createCave/createVisitor Methode: Wo müssen die eigentlich hin? Also wo muss ich die definieren? Im Moment stehen die ja so ganz ohne Klasse dar.

(wenn das jetzt stimmen sollte, habe ich leider noch eine Teilaufgabe b). Aber ich glaube die ist dann nicht mehr so viel Arbeit)

Danke dir auf jeden Fall schon mal für deine bisherige Hilfe!! Das sind ja nun auch immerhin schon 200Zeilen, die du da überfliegen musst :-(

Java:
import java.util.List;
import java.util.Map;

interface caveVisitor{
	void visit(CrossRoads cr);
	void visit(ExitCave ec);
	void visit(EnchantedCave ecC);
	void visit(Pond pond);
	void visit(Cave cave);
}

interface AbstractVisitorFactory {
	public caveVisitor makeVisitor();
}

class PrincessVisitorFactory implements AbstractVisitorFactory{
	public caveVisitor makeVisitor(){
		return new Princess();
	}
}

class FrogVisitorFactory implements AbstractVisitorFactory{
	public caveVisitor makeVisitor(){
		return new Frog();
	}
}

public caveVisitor createVisitor(AbstractVisitorFactory factory){
	caveVisitor = factory.makeVisitor();
	return caveVisitor;
}

class Princess implements caveVisitor{
	int collectedFlowers;
	
	public Princess(){
		collectedFlowers = 0;
	}
	
	public void visit(CrossRoads cr){
		//princesses go right
		this.visit(cr.right);	
	}
	public void visit(ExitCave ec){
		//princesses print the number of flowers they have collected
		System.out.println("I haven collected "+collectedFlowers+"! Bye, bye");
	}
	public void visit(EnchantedCave ecC){
		//picks up a fixed number of flowers
		collectedFlowers += ecC.flowers;
		//visits next cave
		this.visit(ecC.adjacentCave);
	}
	public void visit(Pond pond){
		//visits next cave
		this.visit(pond.adjacentCave);
	}
	public void visit(Cave cave){
		System.out.println("I am in the cave now!");
	}
}

class Frog implements caveVisitor{
	int bugsEaten;
	
	public Frog(){
		bugsEaten = 0;
	}
	public void visit(CrossRoads cr){
		this.visit(cr.left);		
	}
	public void visit(ExitCave ec){
		//frogs print out the number of bugs they have eaten
		System.out.println("I have eaten "+bugsEaten+"! Mjam, mjam!");
	}
	public void visit(EnchantedCave ecC){	
		//visits next cave
		this.visit(ecC.adjacentCave);
	}
	public void visit(Pond pond){
		bugsEaten += pond.bugs;
		//visits next cave
		this.visit(pond.adjacentCave);
	}
	public void visit(Cave cave){
		System.out.println("I am in the cave now!");
	}
}

interface Cave {
	void acceptVisitor(caveVisitor visitor);
}

class CrossRoads implements Cave {
	//i am a composite
	Cave left;
	Cave right;
	
	public CrossRoads(Cave left, Cave right){
		this.left = left;
		this.right = right;
	}	
	public void acceptVisitor(caveVisitor visitor){
		visitor.visit(this);
	}
}


class ExitCave implements Cave {
// i am a leaf
	public ExitCave(Object params){
	}
	public void acceptVisitor(caveVisitor visitor){
		visitor.visit(this);
	}
}

class EnchantedCave implements Cave {
	Cave adjacentCave;
	int flowers;
	public EnchantedCave(Cave cave, int flow){
		this.adjacentCave = cave;
		this.flowers = flow;
	}
	public void acceptVisitor(caveVisitor visitor){
		visitor.visit(this);
	}
}

class Pond implements Cave {
	Cave adjacentCave;
	int bugs;
	public Pond(Cave cave, int bugs){
		this.adjacentCave = cave;
		this.bugs = bugs;
	}
	public void acceptVisitor(caveVisitor visitor){
		visitor.visit(this);
	}
}


interface AbstractCaveFactory {
	public Cave makeCave(Object params);
}

class EnchantedCaveFactory implements AbstractCaveFactory {
    public static final String ADJACENT_CAVE = "cave";
    public static final String FLOWERS = "flowers";
 
    public Cave makeCave(Object params){
        if(!(params instanceof Map)) {
            throw new IllegalArgumentException("Argument must be a Map, see Documentation!");
        }
        // leftout further checks..
        Cave adjacentCave = (Cave) ((Map) params).get(ADJACENT_CAVE);
        int flowers = (Integer) ((Map) params).get(FLOWERS);
        
        return new EnchantedCave(adjacentCave, flowers);
    }   
}

class PondCaveFactory implements AbstractCaveFactory {
    public static final String ADJACENT_CAVE = "cave";
    public static final String BUGS = "bugs";
 
    public Cave makeCave(Object params){
        if(!(params instanceof Map)) {
            throw new IllegalArgumentException("Argument must be a Map, see Documentation!");
        }
        // leftout further checks..
        Cave adjacentCave = (Cave) ((Map) params).get(ADJACENT_CAVE);
        int bugs = (Integer) ((Map) params).get(BUGS);
        
        return new EnchantedCave(adjacentCave, bugs);
    }  
}

class ExitCaveFactory implements AbstractCaveFactory {
	public Cave makeCave(Object params){
		return new ExitCave(params);
	}
}

class CrossRoadsCaveFactory implements AbstractCaveFactory {
	public Cave makeCave(Object params){
		if(!(params instanceof List)){
			throw new IllegalArgumentException("Argument must be a List, see Documentation");
		}
		//leftout further checks
		Cave left = (Cave)((List)params).get(0);
		Cave right = (Cave)((List)params).get(1);
		return new CrossRoads(left,right);
	}	
}

public Cave createCave(AbstractCaveFactory factory, Object params){
	Cave aCave = factory.makeCave(params);
	return aCave;
}
 

Tobias

Top Contributor
Soweit sieht das gut aus. Probier's mal aus - nur beim Draufgucken fallen nämlich sicher nicht alle Fehler auf ;) ... Einfach eine public-Klasse mit einer public static void main() (In dieser Klasse könnte man auch die beiden Methoden unterbringen) erstellen, ein Höhlensystem bauen und ein paarmal unterschiedliche Visitor durchschicken.
 
T

ThomasD.

Gast
Java:
//usage:
public class bla {
	static public void main(String[] args){
		HashMap<String,Object> theobject = new HashMap<String,Object>();
		HashMap<String,Object> theobject2 = new HashMap<String,Object>();
		theobject2.put("cave", createCave(new ExitCaveFactory(),theobject));
		theobject2.put("bugs", 10);
		theobject.put("cave", createCave(new PondCaveFactory(),theobject2));
		theobject.put("flowers",15);
		Cave theCave = createCave(new EnchantedCaveFactory(), theobject);
		
		Frog frog = new Frog();
		theCave.acceptVisitor(frog);
	}
	public static Cave createCave(AbstractCaveFactory factory, Object params){
		Cave aCave = factory.makeCave(params);
		return aCave;
	}
	public static caveVisitor createVisitor(AbstractVisitorFactory factory){
		caveVisitor caveVis = factory.makeVisitor();
		return caveVis;
	}
}

printout: I am in the cave now!

Das passiert wohl, weil createCave eine cave zurück gibt, und der visitor dann die "allgemeine" visit Methode verwendet. Wenn ich aber keine allgemeine visit Methode definiere, kann ich nicht einfach so die adjacentCaves besuchen.. *grummel*
Hm, was tun?

Außerdem ist das erstellen eines CavesSystems so ein bisschen blöde, weil ich das ja total schachteln müsste..aber das ist vielleicht nicht so wichtig....I don't know.
 
T

ThomasD.

Gast
Ich wags jetzt doch schon mal, damits vielleicht am Ende schneller geht.
Teilaufgabe b)
Kluge Frösche verlieren etwas Energie jedes Mal wenn sie eine Höhle besuchen und gewinnen etwas Energie, wenn sie essen.
Solange Frösche noch Energie übrig haben, sind sie "glücklich", sonst sind sie "hungrig". An einer Kreuzungs-Höhle, geht ein hungriger Frosch rechts, während ein glücklicher Frosch links geht. An einer Ausgangshöhle geben kluge Frösche auch aus, ob sie glücklich oder hungrig sind. Unter Benutzung des State Pattern gebe einige Java Klassen an (inkl. abstract classes und interfaces) die das beschriebene Verhalten implementieren.

Meine bisherige Lösung:
Java:
public interface Transitions {
	public Cave chooseCave(CrossRoads cr);
}

public enum State implements Transitions {
	Hungry {	
		public Cave chooseCave(CrossRoads cr){
			return cr.right;
		}
	},
	Happy {
		public Cave chooseCave(CrossRoads cr){
			return cr.left;
		}
	}
}

class SmartFrog extends Frog {
	State state;
	int energy;
	
	public SmartFrog(){
		this.bugsEaten = 0;
		this.energy = 5;
		this.state = State.Happy;
	}
	public void printState(){
		System.out.println("I am "+state);
	}
	
	public void visit(CrossRoads cr){
		energy--;
		this.visit(state.chooseCave(cr));
	}
	public void visit(ExitCave ec){
		energy--;
		//frogs print out the number of bugs they have eaten
		System.out.println("I have eaten "+bugsEaten+"! Mjam, mjam!");
		this.printState();
	}
	public void visit(EnchantedCave ecC){	
		energy--;
		//visits next cave
		this.visit(ecC.adjacentCave);
	}
	public void visit(Pond pond){
		bugsEaten += pond.bugs;
		energy++;
		//visits next cave
		this.visit(pond.adjacentCave);
	}
}
Ich bin mir noch nicht so ganz im Klaren darüber, wie ich den Status wechseln kann. Ich nehme mal irgendwie an, dass wir das auch ohne if-else machen sollen. Aber irgendwie muss ich ja wissen, wie viel Energie so ein Frosch nun hat.
Ich würde dann immer, wenn ich energy verändere aufrufen: this.state.setState(Frog) oder so ähnlich.

Wenn wir das geschafft haben, sind wir fertig! ;-)

Gruß und schönen Abend,
ThomasD.
 

Tobias

Top Contributor
Naja, bei den Caves wirst du ohne Generics wohl direkt casten müssen - also

Java:
public void visit(Cave c) {
    if(c instanceof EnchantedCave) {
        this.visit((EnchantedCave) c);
    }
    // usw
}

Natürlich bekommst du dadurch wieder if-Abfragen, aber das läßt sich hier nicht vermeiden.

Zum State-Pattern: Ein State leitet von seiner Grundklasse ab, deine FrogStates müßten also von Frog erben. Frog leitet dann alle Methodenaufrufe direkt an die aktuelle State-Instanz weiter (Wrapper).
 
T

ThomasD.

Gast
hmm...das mit den Caves gefällt mir nicht.
War ja Teil der ganzen Geschichte das ohne if-else zu machen. Ich glaube ich lass das einfach weg...auch wenn das dann im Endeffekt natürlich irgendwie unvollständig ist :-(

zum State Pattern:
Also wir haben eine Folie/2 Folien da ist das so:

Before:
Java:
public class Door {
   private static final int Opened = 1;
   private static final int Closed = 2;
   int state = Opened;
   public void open() { state = Opened; }
   public void close() { state = Closed; }
   public boolean enter() {
      if (state == Opened)
         return true;
     else if (state == Closed)
        return false;
     else
       throw new Error();
   }
}

After:
Java:
interface State { boolean enter(); }
class Opened implements State {
    public boolean enter() {return true;}}
class Closed implements State {
   public boolean enter() {return false;}}
public class Door {
   private Opened opened = new Opened();
   private Closed closed = new Closed();
   State state = opened;
   public void open() { state = opened; }
   public void close() { state = closed; }
   public boolean enter() {
     return state.enter();
  }
}

So ähnlich habe ich das jetzt versucht:
Java:
interface State {
	public Cave chooseCave(CrossRoads cr);
	public void printState();
}

class Hungry implements State {	
	public Cave chooseCave(CrossRoads cr){
		return cr.right;
	}
	public void printState(){
		System.out.println("I am hungry!");
	}

}
	
class Happy implements State {
	public Cave chooseCave(CrossRoads cr){
		return cr.left;
	}
	public void printState(){
		System.out.println("I am happy!");
	}
}


class SmartFrog extends Frog {
	State state;
	int energy;
	private Hungry hungry = new Hungry();
	private Happy happy = new Happy();
	
	public SmartFrog(){
		this.bugsEaten = 0;
		this.energy = 5;
		this.state = happy;
	}
	public void visit(CrossRoads cr){
		energy--;
		if(energy<=0) state = hungry;
		else state = happy;
		this.visit(state.chooseCave(cr));
	}
	public void visit(ExitCave ec){
		energy--;
		if(energy<=0) state = hungry;
		else state = happy;
		//frogs print out the number of bugs they have eaten
		System.out.println("I have eaten "+bugsEaten+"! Mjam, mjam!");
		state.printState();
	}
	public void visit(EnchantedCave ecC){	
		energy--;
		if(energy<=0) state = hungry;
		else state = happy;
		//visits next cave
		this.visit(ecC.adjacentCave);
	}
	public void visit(Pond pond){
		bugsEaten += pond.bugs;
		energy++;
		if(energy<=0) state = hungry;
		else state = happy;
		//visits next cave
		this.visit(pond.adjacentCave);
	}
}

Naja, zum State-Wechsel ist mir jetzt wirklich nichts besseres eingefallen.
Ist das denn jetzt so überhaupt kein State-Pattern?!

ThomasD.

ps. Wir haben jetzt bis Donnerstag Zeit für die Aufgabe - schöner Aufschub...
 
Status
Nicht offen für weitere Antworten.

Ähnliche Java Themen

Neue Themen


Oben