Flyweight Pattern
Pros
- When you need a large amount of objects, so much so that memory is a concern, you can share commanalities with flyweights
Cons
- Increases complexity and is only needed if memory is a problem
Example Explanation
- Monster - An abstract class which the two concrete flyweights inherit from
- FlyweightZombie - The FlyweightZombie is shared between all MonsterClients with the name "Zombie" so its state does not vary (intrinsic state)
- FlyweightGoblin - The FlyweightGoblin is shared between all MonsterClients with the name "Goblin" so its state does not vary (intrinsic state)
- MonsterFactory - Returns a concrete flyweight for a MonsterClient to use. If the concrete flyweight needed has already been created then it will not create a new one.
- MonsterClient - Keeps track of a Monster's varying state (extrinsic state) while having a reference to a flyweight monster for state that does not vary.
[UML]
import java.lang.*;
import java.util.HashMap;
import java.util.Map;
class Main {
public static void main(String[] args) {
MonsterClient monsters [] = {
new MonsterClient("Zombie", 3),
new MonsterClient("Zombie", 7),
new MonsterClient("Zombie", 11),
new MonsterClient("Goblin", 1),
new MonsterClient("Goblin", 15)
};
//Zombie created
//Goblin created
System.out.println( MonsterFactory.getTotalFlyweightSize()+" flyweight monsters" );
//2 flyweight monsters
}
}
abstract class Monster{
String name;
int maxLife;
int baseAttack;
public Monster(String name, int maxLife, int baseAttack){
this.name = name;
this.maxLife = maxLife;
this.baseAttack = baseAttack;
System.out.println(name+" created");
}
public abstract void move();
public int getMaxLife(){ return maxLife; }
public int getBaseAttack(){ return baseAttack; }
public String getName(){ return name; }
}
class FlyweightGoblin extends Monster{
public FlyweightGoblin(){
super("Goblin", 30, 7);
}
@Override
public void move(){
System.out.println(name+" scampers..");
}
}
class FlyweightZombie extends Monster{
public FlyweightZombie(){
super("Zombie", 50, 4);
}
@Override
public void move(){
System.out.println(name+" trudges..");
}
}
class MonsterFactory{
private static Map<String, Monster> flyweights = new HashMap<>();
public static Monster getMonster(String name){
if(flyweights.containsKey(name)){
return flyweights.get(name);
}
Monster mon;
switch(name)
{
case "Zombie":{ mon = new FlyweightZombie(); break;}
case "Goblin":{ mon = new FlyweightGoblin(); break;}
default: throw new IllegalArgumentException("No monster with that name.");
}
flyweights.put(name, mon);
return mon;
}
public static int getTotalFlyweightSize(){ return flyweights.size(); }
}
class MonsterClient{
int level;
int currentLife;
int bonusLevelAttack;
Monster monster;
public MonsterClient(String name, int level){
this.monster = MonsterFactory.getMonster(name);
this.level = level;
this.currentLife = monster.getMaxLife();
this.bonusLevelAttack = level * 3;
}
public int getAttack(){ return monster.getBaseAttack() + bonusLevelAttack; }
public int getLevel(){ return level; }
public int getLife(){ return currentLife; }
public int getMaxLife(){ return monster.getMaxLife(); }
public String getName(){ return monster.getName(); }
}
Extra Resources