The Flyweight pattern is used for lowering memory usage by sharing as much data as possible with other similar objects.
Let’s say you want to paint a large amount of trees in a landscape:
class Tree {
private:
mesh_;
Mesh bark_;
Texture leaves_;
Texture position_;
Vector double height_;
double thickness_;
barkTint_;
Color leafTint_;
Color };
We notice that the mesh, bark, and leaves are going to be all the same. We’ll leave the barkTint and leafTint as customizable:
class TreeModel {
private:
mesh_;
Mesh bark_;
Texture leaves_;
Texture };
And our tree will look like this:
class Tree {
private:
* model_;
TreeModel
position_;
vector double height_;
double thickness_;
barkTint_;
Color leafTint_;
Color };
Now, when we want to create a forest, we just provide one TreeModel and a Vector of Trees, with each tree pointing to a TreeModel.
We split a component into two parts. The parts that are
context-free
, and the parts that are unique to the
instance.
Let’s try to put down terrain on the earth.
enum class Terrain {
,
Grass,
Hill
River};
The world has a grid of these:
class World {
private:
tiles_[WIDTH][HEIGHT];
Terrain }
and we get some useful data from a tile like so:
int World::getMovementCost(int x, int y) {
switch (tiles_[x][y]) {
case TERRAIN_GRASS: return 1;
case TERRAIN_HILL: return 3;
case TERRAIN_RIVER: return 2;
}
}
bool World::isWater(int x, int y) {
switch (tiles_[x][y]) {
case TERRAIN_GRASS: return false;
case TERRAIN_HILL: return false;
case TERRAIN_RIVER: return true;
}
}
Instead, let’s make a terrain class.
class Terrain {
public:
(int movementCost, bool isWater, Texture texture)
Terrain: movementCost_(movementConst),
isWater_(isWater),
texture_(texture) {}
int getMovementCost() const { return movementCost_; }
bool isWater() const { return isWater_; }
const Texture& getTexture() const { return texture_; }
private:
int movementCost_;
bool isWater_;
texture_;
Texture };
Because this has nothing that’s coupled to the location of the tile, let’s just generate one tile of each type and maintain pointers to it in the world instance.
class World {
private:
* tiles_[WIDTH][HEIGHT];
Terrain};
Because this is used in multiple places, hoist the lifetime of these terrain pieces:
class World {
public:
()
World: grassTerrain_(1, false, GRASS_TEXTURE),
hillTerrain_(3, false, HILL_TEXTURE),
riverTerrain_(2, true, RIVER_TEXTURE)
{}
private:
grassTerrain_;
Terrain hillTerrain_;
Terrain riverTerrain_;
Terrain };
Then we can paint the ground like so:
void World::generateTerrain() {
for (int x = 0; x < WIDTH; x++) {
for (int y = 0; y < HEIGHT; y++) {
// Make hills
if (random(10) == 0) tiles_[x][y] = &hillTerrain_;
else tiles_[x][y] = &grassTerrain_;
}
}
// Make a river
int x = random(WIDTH);
for (int y = 0; y < HEIGHT; y++) {
tiles_[x][y] = &riverTerrain_;
}
}
Now instead of a method on World
for accesssing the
terrain properties, expose the Terrain object directly.
const Terrain& World::getTile(int x, int y) const {
return *tiles_[x][y];
}