Friday, June 10, 2022

Design Pattern: Flyweight Pattern

Chapters

Flyweight Pattern

Flyweight pattern refers to an object that minimizes memory usage by sharing some of its data with other similar objects. The flyweight pattern is useful when dealing with large numbers of objects with simple repeated elements that would use a large amount of memory if individually stored.

This example demonstrates flyweight pattern.
import java.util.Queue;
import java.util.ArrayDeque;

public class ClientCode{

  public static void main(String[] args){
    int x = 0;
    int y = 0;
    
    TileFlyWeightFactory tileFactory =
    TileFlyWeightFactory.getInstance();
    
    //4x4 grid
    for(int i = 0; i < 4; i++){
      for(int j = 0; j < 4; j++){
        tileFactory.
        useThenAdd().
        draw(x, y);
        x += 64;
      }
      x = 0;
      y += 64;
      System.out.println();
    }
    
  }
}

interface TileInterface{

  void draw(int posx, int posy);
}

class Tile implements TileInterface{
  //intrinsic state
  private final String tileSource;
  
  Tile(String tileSource){
    this.tileSource = tileSource;
  }
  
  @Override
  public void draw(int posx, int posy){
    System.out.println
    (tileSource + " is drawn at " +
     posx + ", " + posy);
  }
  
}

//singleton
class TileFlyWeightFactory{
  private static TileFlyWeightFactory instance;
  private Queue<TileInterface> cache;
  
  private TileFlyWeightFactory(){
    cache = new ArrayDeque<>();
    cache.add(new Tile("Tile1.jpg"));
    cache.add(new Tile("Tile2.jpg"));
    cache.add(new Tile("Tile3.jpg"));
    cache.add(new Tile("Tile4.jpg"));
  }
  
  public static TileFlyWeightFactory getInstance(){
    if(instance == null)
      instance = new TileFlyWeightFactory();
    return instance;
  }
  
  public TileInterface useThenAdd(){
    TileInterface target = null;
    
    if(!cache.isEmpty()){
      target = cache.poll();
      cache.add(target);
    }
    return target;
  }
  
}

Result
Tile1.jpg is drawn at 0, 0
Tile2.jpg is drawn at 64, 0
Tile3.jpg is drawn at 128, 0
Tile4.jpg is drawn at 192, 0

Tile1.jpg is drawn at 0, 64
Tile2.jpg is drawn at 64, 64
Tile3.jpg is drawn at 128, 64
Tile4.jpg is drawn at 192, 64

Tile1.jpg is drawn at 0, 128
Tile2.jpg is drawn at 64, 128
Tile3.jpg is drawn at 128, 128
Tile4.jpg is drawn at 192, 128

Tile1.jpg is drawn at 0, 192
Tile2.jpg is drawn at 64, 192
Tile3.jpg is drawn at 128, 192
Tile4.jpg is drawn at 192, 192
First off, we store two states to our flyweight class (Tile class in this case). One of them is the intrinsic state. This state is constant. The next one is the extrinsic state. This state is not constant and likely to change overtime. In the example above, tileSource variable is the intristic state; posx and posy are extrinsic states.

In the example above, I reuse instantiated Tile objects in order to reuse their tileSource constants (intrinsic) and draw them anywhere on the screen using posx and posy variables (extrinsic). This technique saves a lot of system resources because I don't need to create a new instance of Tile class in order to reuse a tile image that's been already used.

Other information such as caching, retrieval, concurrency can be found in this wiki.

No comments:

Post a Comment