Programmeren in Java/Design Patterns/Strategy Pattern
Strategy Pattern laat toe om een algoritme dat door een programma wordt gebruikt makkelijk te vervangen door een ander terwijl het programma draait. Deze algoritmes worden dan gezien als een familie van algoritmes. Ieder algoritme wordt in een apart object ingekapseld en wordt ervoor gezorgd dat ze makkelijk kunnen worden verwisseld met elkaar.
Zonder Strategy Pattern
bewerkenStel we hebben een Object genaamd Vogel . In dat object zijn er allerlei methodes zoals eten(), drinken() en vliegen(). Veronderstel dat alle vogels op dezelfde manier eten, drinken en vliegen dan zal de klasse van ons object misschien als volgt uit zien.
Java-code:
public class Vogel {
public Vogel(){
}
public void eten(){
System.out.println("Vogel eet.");
}
public void drinken(){
System.out.println("Vogel drinkt.");
}
public void vliegen(){
System.out.println("Vogel vliegt.");
}
}
Er is echter een probleem. In de natuur zijn er vogels die vliegen zoals mussen, meeuwen, ... enzoverder, maar er zijn ook vogels die niet kunnen vliegen zoals kippen, struisvogels, ... enzoverder. We kunnen dan gebruik maken van overerving. We gebruiken dan het object Vogel als een superklasse en maken dan subklasses van een Mus, Meeuw, Kip, Struisvogel,... We laten dus die klasses van Vogel overerven en bij de vogels die niet kunnen vliegen overridden we de methode vliegen(). Hieronder is de klasse van een vogelsoort die wel kan vliegen namelijk de mus.
Java-code:
public class Mus extends Vogel {
public Mus(){
super();
}
}
Omdat mussen kunnen vliegen, hoeven we de methode vliegen() niet aan te passen. Nu gaan we een klasse maken van een vogelsoort die niet kan vliegen namelijk de kip.
Java-code:
public class Kip extends Vogel {
public Kip(){
super();
}
public void vliegen(){
System.out.println("Vogel kan niet vliegen.");
}
}
De code zal prima werken. Maar het probleem is dat veel code zullen moeten maken met kopieren, stel u voor dat we 1000 klasses hebben van vogels die niet kunnen vliegen dan moeten we dit stukje code 1000 keer er bij schrijven. En wat als we dan een fout in al die 1000 vogels hebben dan moeten we ze één voor één aanpassen. Ook een probleem is, is dat het niet dynamisch is: een mus kan vliegen, maar wat als het gewond geraakt? Dan kan hij misschien niet meer vliegen. Of we willen een verschil maken tussen vogels die heel ver kunnen vliegen en vogels die slechts enkele kilometers kunnen vliegen, dan moeten we nog meer code herschrijven. Hiervoor dient dus strategy pattern.
Met Strategy Pattern
bewerkenWe zetten de algoritmes van de methode vliegen() apart. We hebben daarvoor eerst een interface nodig genaamd Vlieg.
De interface
bewerken- Zie ook interface voor meer informatie.
Deze interface zal vliegen() bevatten maar omdat het een interface is kan er geen code worden bij geschreven. Onze interface ziet er als volgt uit:
Java-code:
public interface Vlieg {
abstract void vliegen();
}
Subklassen van de interface
bewerkenNu gaan we 2 klasses maken KanVliegen voor vogels die kunnen vliegen en KanNietVliegen voor vogels die niet kunnen vliegen. Hieronder bevindt zich de klasse voor vogels die kunnen vliegen.
Java-code:
public class KanVliegen implements Vlieg{
public void vliegen(){
System.out.println("Deze vogel kan vliegen.");
}
}
En hieronder voor vogels die niet kunnen vliegen.
Java-code:
public class KanNietVliegen implements Vlieg{
public void vliegen(){
System.out.println("Deze vogel kan niet vliegen");
}
}
Allemaal goed en wel, maar hoe implementeren we zoiets in de klasse Vogel en zijn subklassen? Het is heel simpel.
De interface en zijn subklassen gebruiken
bewerkenLaten we eerst kijken naar de code voor Vogel.
Java-code:
public class Vogel {
Vlieg vlieg;
public Vogel(){
}
public void eten(){
System.out.println("Vogel eet");
}
public void drinken(){
System.out.println("Vogel drinkt");
}
public void vliegen(){
this.vlieg.vliegen();
}
}
Van de interface Vlieg kunnen we objecten aanmaken. In dit geval maken we er één aan met de naam vlieg. En dankzij polymorfisme kunnen we objecten van KanVliegen en KanNietVliegen er in opslaan. De methode vliegen() hebben we laten staan, maar hier roepen we de methode vliegen() op van de interface. Oké, we hebben dit niet opgeslagen bij het object vlieg en hebben natuurlijk daardoor geen onderscheid gemaakt tussen een mus en een kip. Dit doen we in de subklassen.
Java-code:
public class Mus extends Vogel {
public mus(){
super();
this.vlieg = new KanVliegen();
}
}
Bij de constructor van de klasse Mus slaan we bij vlieg een object van KanVliegen op die de code bevat voor vogels die wel kunnen vliegen, dit doen we ook bij Kip maar dan met KanNietVliegen.
Java-code:
public class Kip extends Vogel {
public Kip(){
super();
this.vlieg = new KanNietVliegen();
}
}
Testklasse
bewerkenNu moeten we alleen nog zien of het werkt.
Java-code:
public class test {
public static void main(String args[]){
Vogel v1 = new Kip();
Vogel v2 = new Mus();
System.out.println("Kip");
v1.vliegen();
System.out.println("\nMus");
v2.vliegen();
}
}
Als je deze code uitvoert, krijg je dit als ouput:
Kip Deze vogel kan niet vliegen Mus Deze vogel kan vliegen
Veranderen
bewerkenHeel belangrijk aan Strategy Pattern is dat je dynamisch code kunt veranderen in je programma. Dit wil zeggen dat wanneer je je programma draait je ervoor kunt zorgen dat je programma andere code gebruikt dan het normaal zou moeten uitvoeren. Hier volgt een voorbeeld:
Java-code:
public class Vogel {
Vlieg vlieg;
public Vogel(){
}
public void eten(){
System.out.println("Vogel eet");
}
public void drinken(){
System.out.println("Vogel drinkt");
}
public void vliegen(){
this.vlieg.vliegen();
}
public void geraaktGewond(){
this.vlieg = new KanNietVliegen();
}
}
Bij de klasse Vogel hebben we een extra methode toegevoegd namelijk geraaktGewond(). Als een vogel gewond geraakt, kan het niet meer vliegen. Als we een Mus hebben dan bevat vlieg een KanVliegen object zodra hij gewond geraakt, wordt die vervangen door een KanNietVliegen object. Het spreekt voor zich dat als een kip gewond geraakt, deze methode geen effect zal hebben.
Java-code:
public class test {
public static void main(String args[]){
Vogel v1 = new Mus();
System.out.println("Mus");
v1.vliegen();
v1.geraaktGewond();
System.out.println("\nMus");
v1.vliegen();
}
}
Dit is de testklasse we maken een Mus-object aan. We laten hem vliegen, daarna geraakt hij gewond en laten we opnieuw vliegen. Dit is wat je dan krijgt als output.
Mus Deze vogel kan vliegen Mus Deze vogel kan niet vliegen
Conclusie
bewerkenDit voorbeeld is klein en heeft niet veel code, daardoor lijkt het alsof het niet veel verschil maakt. Wanneer je met grote stukken programmacode werkt, zal je merken dat het wel degelijk verschil maakt. Je code komt overzichtelijker over, minder typwerk en veel makkelijker om aan te passen.