Entwurfsmuster Strategie

Termii

Mitglied
Hallo,
ich hänge bei dieser Aufgabe schon rund 10 Stunden fest. Es ist eine Aufgabe meiner Uni (1.Semester Programmierung), wozu es keine Musterlösung gibt.

Aufgabe:
Die Klasse ZeroData soll als Unterklasse der Klasse Data implementiert werden und die folgende Methode bereitstellen:
public int zeros() soll die Häufigkeit des Auftretens des Wertes 0 unter den im Attribut ints abgelegten Werten bestimmen und zurückgeben.

Java:
//Diese Klasse darf nicht verändert werden
import java.util.*;
public class Data
{
    private List<Integer> ints;
    public Data(int [] a){
        ints = new LinkedList<>();
        for(int i:a){
            ints.add(i);
        }    
    }
    protected static interface Strategy{
        void doIt(int i);
    }
    protected void loop(Strategy s){
        for(int i :ints){
            s.doIt(i);
        }
    } 
}
Java:
public class ZeroData
{
}

Die Klassen habe ich so gelassen, wie sie mir gegeben wurden. Ich möchte nicht falsches Ergänzen, da es dem Helfer nur mehr Arbeit machen würde.
 
Zuletzt bearbeitet:

lordofdonuts

Aktives Mitglied
Hallo Termii,

ja, das ist ein gemeines Beispiel :D

Ich werde dir hier nicht die ganze Lösung verraten, aber beschreiben was du tun musst.

  1. ZeroData von Data ableiten
  2. Interface implementieren
  3. Ein Attribut vom Typ int dazuerfinden
  4. doIt(int) was sinnvolles machen lassen (es könnte ja die 0er zählen)
  5. zeros() implementieren (es könnte ja eine Methode der Superklasse aufrufen)

Den letzten Schritt lass ich absichtlich weg, weil ich noch gemeiner bin als das Beispiel. Bin mir sicher, du kommst da von selber drauf.

Auf die schnelle würd ichs ca. so machen:

Java:
public class ZeroData extends Data
{
    public ZeroData(int[] a) {
        super(a);
    }

    public int zeros() {
        // ...
    }

    private class ZeroDataStrategy implements Strategy
    {
        private int zeros = 0;
    	
        @Override
        public void doIt(int i) {
            // something magical happens
        }
		
        public int getZeros() {
            return zeros;
        }
    }
}

Falls du aber echt keinen Plan hast, kannst du gern für einen Keks einen Hinweis kaufen.
 

qexxler

Aktives Mitglied
Schönen guten Morgen zusammen,

da ich die Aufgabe interssant finde, habe ich mich auch an dieser Versucht, da ich selbst noch nicht so viel mit Strategy-Muster gearbeitet habe.

Hier mein aktueller Vorschlag, der sicherlich nicht ganz gut ist. Für Verbesserungsvorschläge bin ich sehr gerne offen.

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

public class Data {

	protected Strategy strategy = null;
	private List<Integer> ints;

	public Data(int[] a) {
		ints = new LinkedList<>();
		for (int i : a) {
			ints.add(i);
		}
	}

	public void setStrategy(final Strategy strategy) {
		if (strategy == null)
			throw new NullPointerException("strategy == null");
		this.strategy = strategy;
	}

	public Strategy getStrategy() {
		return strategy;
	}
	

	protected static interface Strategy {
		void doIt(int i);
	}

	protected void loop(Strategy s) {
		for (int i : ints) {
			s.doIt(i);
		}
	}
}

und


Java:
public class ZeroData extends Data {

	public ZeroData(int[] a) {
		super(a);
		strategy = new ZeroDataStrategy();
	}

	public int zeros() {
		loop(strategy);
		return ((ZeroDataStrategy) strategy).getZeros();
	}

	private class ZeroDataStrategy implements Strategy {

		private int zeros = 0;

		@Override
		public void doIt(int i) {
			if (i == 0)
				zeros++;
		}

		public int getZeros() {
			return zeros;
		}

	}

}

Was mir persönlich an meiner Lösung überhaupt nicht gefällt ist die Zeile 10 in der ZeroData Klasse. Kann mir einer helfen?
 

lordofdonuts

Aktives Mitglied
Was mir persönlich an meiner Lösung überhaupt nicht gefällt ist die Zeile 10 in der ZeroData Klasse. Kann mir einer helfen?

Klar... zunächst einmal besteht ja die Anforderung, die Data-Klasse nicht zu ändern, d.h. du kannst den getter und den setter bzw. das Attribut vom Typ Strategy weg lassen. Es reicht, in zeros() ein ZeroDataStrategy-Objekt zu erzeugen und zu übergeben.

Und das ist eigentlich schon der ganze Zauber.
 

qexxler

Aktives Mitglied
Vielen Dank für deine Anmerkungen.

Würde das also bedeuten, dass meine Methode wiefolgt aussehen müsste:

Java:
	public int zeros() {
		ZeroDataStrategy zs = new ZeroDataStrategy();
		loop(zs);
		return zs.getZeros();
	}

Wie du schon richtig angemerkt hast, waren die getter und setter falsch bzw. hätten nicht implementiert werden dürfen.

Aber irgendwie habe ich das Strategy-Muster so im Kopf, als das man zur Laufzeit die Algorithmen dynamisch ändern kann und sollte. Deswegen habe ich extra in der Data Klasse die enstsprechende Instanz-Variable auf den Interface Typ gesetzt. Ist dieser Ansatz im groben falsch, oder nur in diesem konkreten Fall nicht angebracht?

Werde bei einer Tasse Kaffee darüber nachdenken und freue mich natürlich auch weiterhin über jede Hilfe, die mich weiter bringt.
 

Termii

Mitglied
Java:
import java.util.*;
public class ZeroData extends Data{
     
    public ZeroData(int[] a) {
        super(a);
    }
    public class ZeroDataStrategy implements Strategy
    {
        Strategy zeroStrategy = new ZeroDataStrategy();
        private int zeros = 0;
    
        @Override
        public void doIt(int i) {
            if(i == 0){
                zeros++;
            }
        }
            
        public int getZeros() {
            loop(zeroStrategy);
            return zeros;
        }
    }
}
//Die Methode loop muss ein Startegy Objekt übergeben bekommen. 
//In diesem Objekt muss loop die Variable ints finden können. 
//Wo bekomme ich die her?
 
Zuletzt bearbeitet:

lordofdonuts

Aktives Mitglied
@qexxler

Nein, du hast schon recht. Damit das Beispiel "schön" wäre, müsste die Data-Klasse ein Attribut vom Typ Strategy haben, was sie aber nicht tut. Dementsprechend könntest du dann Klassen von Strategy ableiten und verschiedene Logiken realisieren. Das ganze nennt sich Polymorphie :rtfm:

Was mich irretiert, ist, dass unbedingt eine ZeroData-Klasse existieren muss, die von Data abgeleitet ist. Das haut dir nämlich das Entwurfsmuster durcheinander. Ansonsten wär das Beispiel watscheneinfach.


@Termii
Der Patschen fangt schon einmal da an:

Java:
public class ZeroDataStrategy implements Strategy
    {
        Strategy zeroStrategy = new ZeroDataStrategy();

Das gehört in ZeroData.

Java:
loop(zeroStrategy);

Das gehört auch in ZeroData und zwar in die zeros()-Methode.
 

qexxler

Aktives Mitglied
Vielen Dank für deine schnelle Antwort.

Ich habe mir das Buch "Entwurfsmuster von Kopf bis Fuß" geliehen und dort das Strategy-Muster angesehen. Daher kenne ich dieses. Dort war es so, dass es eine Klasse Hund gab, die als abstrakt deklariert wurde und diese zwei Instanz-Variablen hatte. Zum einen für das BellVerhalten und für noch etwas. In der Klasse Hund war es dann auch so, dass man direkt das Verhalten als setter Setzen konnte und somit zur Laufzeit entsprechendes Verhalten ändern konnte.

Habe die Definition des Strategy-Musters nicht im Kopf, aber ich habe mir gemerkt, dass sich der Code bzw. die Implementierung, die sich ändern, dass eben dieser ausgelagert werden sollte. Deswegen auch mein obiger, erster Ansatz.

Mit deiner Anmerkung hast du Recht.

Glücklicherweise weiß ich, was genau Polymorphie ist, sonst wäre ich ausgeschmissen. ;-)

Aber eines kann ich dir sagen: Mach weiter so, wie bisher. Solche Leute wie dich, die gerne helfen, braucht man. Du hast mir, obwohl es nicht einmal meine Aufgabe war, sehr geholfen.

Würdest du hier wohnen, gäbe es nun einen Kaffee und ja, auch gerne einen Keks. Danke dir. :toll:
 

Termii

Mitglied
Lösung (getestet):
Java:
import java.util.*;
public class Data
{ 
    public List<Integer> ints;
    public Data(int [] a){
        ints = new LinkedList<>();
        for(int i:a){
            ints.add(i);
        }
    }
    
    protected static interface Strategy{
        void doIt(int i);
    }
    
    protected void loop(Strategy s){
        for(int i :ints){
            s.doIt(i);
        }
    }
    
}

Java:
import java.util.*;
public class ZeroData extends Data{
    ZeroDataStrategy zeroStrategy = new ZeroDataStrategy();
    private int zeros = 0;
    public ZeroData(int[] a) {
        super(a);
    }
    
    public int getZeros() {
        loop(zeroStrategy);
        return zeros;
    }
    
    public class ZeroDataStrategy implements Strategy
    {
        
        @Override
        public void doIt(int i) {
            if(i == 0){
                zeros++;
            }
        }     
    }
}

@lordofdonuts, Ich schließe mich qexxler an. Ich bin dir wirklich extrem dankbar für die Hilfe und schnellen Antworten! Mit der Lösung habe ich nun zumindest eine Grundlage andere Aufgaben anzugehen.
 
Zuletzt bearbeitet:

qexxler

Aktives Mitglied
Ich bin mir nicht ganz sicher, ob das sinnvoll ist, wenn du die Instanz-Variable zeros in die ZeroData Klasse ziehst, da diese mit dem Objekt eigentlich nichts zu tun hat. Sie soll die Logik im eigentlichen Algorithmus kappseln. Daher wäre es sinnvoller, die Variable wie in die ZeroDataStrategy zu ziehen und dort auch eine entsprechende get - Methode zu schreiben, die dir den Wert zurückliefert. Die Methode für das ZeroData Objekt stand bereits oben irgendwo.
 

Ähnliche Java Themen

Neue Themen


Oben