Imagine that you are developing some gaming software. Your write Web client and on each of response you are parsing entire XML to get your game Units. You have some set of types of Units, for example 50 different animals, but when you parse your XML you can get dozens of instances of the same Unit and few dozens of instances of other Unit.
If User of the game is very passionate gamer, he could send requests very frequently. In this case your application will be creating dozens of instances for each of the Unit. But, your units have some static descriptions. For example, Dragon has Attack, initial Health level, and also you need to keep image of the dragon in the object of Dragon.
This all lead to intensive and not efficient memory usage. How could you share common information for all types of Units without creating instances for each individual Unit?
FlyWeight
1) Simplest way with creating objects each time.
We have base class Unit:
public class Unit {
protected String name;
protected int health;
protected String picture;
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setHealth(int health) {
this.health = health;
}
public int getHealth() {
return health;
}
public void setPicture(String picture) {
this.picture = picture;
}
public String getPicture() {
return picture;
}
}
And two derived - Dog and Dragon. To make those objects more weightfull I added to them picture. In my case that is only very long string.
public class Dog extends Unit{
public Dog(){
name = "dog";
health = 30;
for(int i = 0; i < 100; ++i)
picture += "I don't want to load actuall image, but if we will be" +
"creating a lot of strings on each of the Unit this could be very" +
"resrouce taking operation.";
}
}
And our parser executes code which looks like:
public class Parser {
public ArrayList<Unit> parse(){
ArrayList<Unit> result = new ArrayList<Unit>();
for(int i = 0; i < 150; ++i)
result.add(new Dragon());
for(int i = 0; i < 600; ++i)
result.add(new Dog());
System.out.println("Dogs and Dragons are parsed.");
return result;
}
}
We want to create only 150 Dragons and 600 Dogs and this takes 28 Mb of memory.
2) How does FlyWeight work?
Lets introduce UnitsFactory. Responsibility of this factory is to manage creation of the concrete flyweight objects (Dogs and Dragons). It verifies if the object has been already created and in this case it just returns created and if not it creates new one and returns it. See:
public class UnitsFactory {
private static Map<Class, Unit> _units = new WeakHashMap<Class, Unit>();
public static Dog createDog(){
Class dogClass = Dog.class;
if(! _units.containsKey(dogClass))
_units.put(dogClass, new Dog());
return (Dog) _units.get(dogClass);
}
public static Dragon createDragon(){
Class dragonClass = Dragon.class;
if(! _units.containsKey(dragonClass))
_units.put(dragonClass, new Dragon());
return (Dragon) _units.get(dragonClass);
}
}
Lets take a look on UML diagram of our code:
UnitsFactory corresponds to FlyweightFactory, Unit - for Flyweight. Dog, Dragon corresponds to concrete Flyweights in the GoF FlyWeithgt naming.
Now we will do change to our parser to use Factory and will see how much of memory will it take. But now we are going to create 1500 Dragons and 60000 Dogs, probably your gamer is quite more quick as you think.
for(int i = 0; i < 1500; ++i)
result.add(UnitsFactory.createDragon());
for(int i = 0; i < 60000; ++i)
result.add(UnitsFactory.createDog());
And this takes only about 5 Mb of memory:
What have I learned regarding to of Java?
I know that corresponding to C# Dictionary is Map in Java, and there are few concrete maps like on this good picture:
Hope this was a good story!
Go to: My Design Patterns Table
|
|
 |
 | So all the units have the same health? fredito | 4:06 17 Feb '10 |
|
 |
Apparently there are 600 dogs. If one gets hit by 5 health points, all 600 dogs get weaker. The design is awesome if the units are enemies, but that kinda stink if they are friendly
I'm guessing the article will have another installment to differentiate the units, right?fredito
|
|
|
|
 |
|
 |
I'm sorry, my example is not so clear.
The main purpose of Flyweight is to utilize/share same instances of massive objects, this could be needed when you need to have a lot of similar objects that share same resource like image for Dragon in memory.
So you are correct that current design is bad especially for game.
Better is to have UnitImagesFactory, which is flyweight factory, and it can return Images of Dragon or either Dog, so only one allocation of memory for each image. This factory should/will be used in constructor of particular Unit.
This way all properties will be different instances and each Unit will have it's health. Only image of Dragon will be the same instance for all Dragons.
fredito, is my design better now?
|
|
|
|
 |
|
 |
True, image caching is always a good strategy.
Actually I thought of your example a bit more and really all depends on the point of view. If I rename "health" into "MaxHealth", then the value becomes constant (so we really want to mark the properties as "final"). The flyweight becomes a static unit property class that can be referenced by each individual units. And we are good again. fredito
|
|
|
|
 |
 | can you explain...? Herre Kuijpers | 22:22 8 Feb '10 |
|
 |
i'm not too familiar with java, however in your article you point out there is a major difference between the memory usage with respect to the approach used.
I fail to understand why in the first method the creation of a mere 750 objects would take 26MB of memory while in the second approach the creation of 61500 objects would take only 5MB.... can you explain please?
tnxHerre
|
|
|
|
 |
|
 |
Herre, actually it doesn't really create 61500 objects but only two (one for Dog and one for Dragon). The main purpose of Flyweight is to utilize/share same instances of massive objects, this could be needed when you need to have a lot of similar objects that share same resource like image for Dragon in memory.
So you can rewrite my code in order that all instances of Dragon will share same picture in the memory. Only some other properties will differ when you work with concrete Dragons.
FROM CODE: Following two lines take a look if instance of Dragon is already in container and if not it creates new one and put it into _units. if(! _units.containsKey(dragonClass)) _units.put(dragonClass, new Dragon()); Following returns existing on return (Dragon) _units.get(dragonClass);
I'm afraid that my example is not quite clear.
Now I think it was better to introduce ImageFactory and have there "public static Image getDragonImage(){..." which could be used in constructor of Dragon. (this looks more realisticly)
Did I explain it more clearly?
|
|
|
|
 |
|
 |
i'm sorry to say that that point wasnt very clear from your article. I have to admit i did not have a look at the actual code itself. However if I understand correctly, your implementation checks a hashtable to see if the object was already created, if so it will return the object, if not it will create it instantly and insert it into the hastable.
Obviously this will take less memory initially, however what happens of all objects are retrieved from the hashtable 1 by 1? I suppose you would need some kind of clean-up mechanism or you would run out of memory...?
Come to think of it, this more sounds like a caching mechanisms.. Am I correct?
cheers,Herre
|
|
|
|
 |
|
|
Last Updated 8 Feb 2010 |
Advertise |
Privacy |
Terms of Use |
Copyright ©
CodeProject, 1999-2010