Flyweight Design Pattern - an Implementation

Flyweight Design Pattern is one of the Structural design pattern. This pattern decreases object count in heap by reusing Objects from an Object pool. This will improve application’s memory management. According to GoF, flyweight design pattern’s intent is:

Use sharing to support large numbers of fine-grained objects efficiently.

Flyweight design pattern is useful when we need to create a lot of Objects of a class. As every object consumes some memory space so reusing Objects can save a lots of memory. This efficient way of memory management is very useful for low memory devices such as mobile or embedded systems. Following improvement can be achieved by using Flyweight pattern.

  • It reduces the number of Objects. Less number of objects reduces the memory usage, and it keep us away from memory related error like java.lang.OutOfMemoryError.
  • Although creating an object in Java is really fast, we can still reduce the execution time of our program by sharing objects.

Now let us have a look on all the participants that needs to be implemented for Flyweight design pattern .

  • Flyweight - Declares an interface through which flyweights can receive and act on extrinsic state.

  • ConcreteFlyweight - Implements the Flyweight interface and stores intrinsic state. A ConcreteFlyweight object must be sharable. The Concrete flyweight object must maintain state that it is intrinsic to it, and must be able to manipulate state that is extrinsic. These are the contrete VideoList implementations in our example named as ActionVideosComedyVideos and WarVideos.

  • FlyweightFactory - The factory creates and manages flyweight objects. In addition the factory ensure sharing of the Flyweight objects. The factory maintains a pool of different Flyweight objects and returns an object from the pool if it is already created, adds one to the pool and returns it in case it is new. In the movie database example the Flyweight factory can create three types of flyweights : a ActionVideos Flyweight, a ComedyVideos Flyweight as well as a WarVideos Flyweight. When the Client asks the Factory for a movie list by its genre, the factory checks to see if there is a movie list in the pool of that genre, if there is, it is returned to the client, if there is no movie list in pool of that genre, a movie list is created of that genre, added to pool, and returned to the client, the next time a client asks for a movie list of that genre, the movie list created previously is returned, no new movie list is created.

  • Client - A client maintains references to flyweights in addition to computing and maintaining extrinsic state.

 

 

Example:

Here we are going to develop a database of movies. Where the movies are categorized by it’s Genres. User can ask for a list of movies by genre, and the System will return the same. But for memory efficiency we want to keep a single copy of List for each genre by using the Flyweight pattern. The movie list is consist of a collection of videos represented by the following Video Object.

package com.dp.flyweight;

import java.util.Random;

public class Video {

	private int id;
	private String movieName;
	private String imdbUrl;

	public Video(String movieName, String imdbUrl) {
		super();
		Random rand = new Random();
		this.id = rand.nextInt(50) + 1;
		this.movieName = movieName;
		this.imdbUrl = imdbUrl;
	}

	public void printVideoMetaData() {
		System.out.println("---------- Movie : " + this.movieName + " --------");
		System.out.println("| videoId:" + id);
		System.out.println("| imdbUrl:" + imdbUrl);
		System.out.println();
	}
}

Now the videos are saved by genre,The Genre is represented by the following enum Genre.

package com.dp.flyweight;

public enum Genre {

	ACTION,ROMANCE,COMEDY,WAR;
}

Now, let us declare the Flyweight interface VideoList.java through which flyweights can receive and act on extrinsic state. All Concrete implementation of this interface represents a list of movie data with it’s Genre.

package com.dp.flyweight;

public interface VideoList {

	void getDetails();
}

Now let us implement the different type of Genre like Action, Comedy and War movies. 

## ACTION Movies
package com.dp.flyweight;

import java.util.ArrayList;
import java.util.List;

public class ActionVideos implements VideoList {

	private Genre genre;
	private List<Video> videos;

	public ActionVideos() {
		this.genre = Genre.ACTION;
		videos = new ArrayList<>();
		videos.add(new Video("Maze Runner", "https://www.imdb.com/title/tt1790864/"));
		videos.add(new Video("Black Panther", "https://www.imdb.com/title/tt1825683/"));
		videos.add(new Video("Tomb Raider", "https://www.imdb.com/title/tt1365519/"));
	}

	@Override
	public void getDetails() {
		System.out.println("Genre : Action");
		System.out.println("##############");
		videos.forEach(v -> v.printVideoMetaData());
		System.out.println();
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((genre == null) ? 0 : genre.hashCode());
		result = prime * result + ((videos == null) ? 0 : videos.hashCode());
		return result;
	}
}

##################################################################

## COMEDY Movies
package com.dp.flyweight;

import java.util.ArrayList;
import java.util.List;

public class ComedyVideos implements VideoList {

	private Genre genre;
	private List<Video> videos;

	public ComedyVideos() {
		this.genre = Genre.COMEDY;
		videos = new ArrayList<>();
		videos.add(new Video("Toy Story", "https://www.imdb.com/title/tt0114709/"));
		videos.add(new Video("The Cable Guy", "https://www.imdb.com/title/tt0115798/"));
	}

	@Override
	public void getDetails() {
		System.out.println("Genre : Comedy");
		System.out.println("##############");
		videos.forEach(v -> v.printVideoMetaData());
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((genre == null) ? 0 : genre.hashCode());
		result = prime * result + ((videos == null) ? 0 : videos.hashCode());
		return result;
	}
}

##################################################################

## WAR Movies
package com.dp.flyweight;

import java.util.ArrayList;
import java.util.List;

public class WarVideos implements VideoList {

	private Genre genre;
	private List<Video> videos;

	public WarVideos() {
		this.genre = Genre.WAR;
		videos = new ArrayList<>();
		videos.add(new Video("Saving Private Ryan", 
				"https://www.imdb.com/title/tt0120815/"));
		videos.add(new Video("Enemy at the Gates", 
				"https://www.imdb.com/title/tt0215750/"));
		videos.add(new Video("Pearl Harbor", 
				"https://www.imdb.com/title/tt0213149/"));
	}

	@Override
	public void getDetails() {
		System.out.println("Genre : War");
		System.out.println("##############");
		videos.forEach(v -> v.printVideoMetaData());
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((genre == null) ? 0 : genre.hashCode());
		result = prime * result + ((videos == null) ? 0 : videos.hashCode());
		return result;
	}
}

Now let us create the factory as VideoFactory.java. This factory creates and manages the Flyweight objects. In addition the factory ensure sharing of the Flyweight objects. The VideoFactory maintains a pool of VideoList type objects and returns an object from the pool if it is already exists or adds one to the pool and returns in case of the first request of that genre.

package com.dp.flyweight;

import java.util.HashMap;
import java.util.Map;

public class VideoFactory {

	private Map<Genre, VideoList> videoCache = new HashMap<>();

	public VideoList getVideoList(Genre genre) {
		VideoList videoList = videoCache.get(genre);
		if (videoList == null) {
			if (genre == Genre.WAR) {
				videoList = new WarVideos();
				videoCache.put(Genre.WAR, videoList);
			}
			if (genre == Genre.COMEDY) {
				videoList = new ComedyVideos();
				videoCache.put(Genre.COMEDY, videoList);
			}
			if (genre == Genre.ACTION) {
				videoList = new ActionVideos();
				videoCache.put(Genre.ACTION, videoList);
			}
		}
		return videoList;
	}
}

Now let us write the client to test the pattern as Test.java

package com.dp.flyweight;

public class Test {

	public static void main(String[] args) {

		VideoFactory videoFactory = new VideoFactory();

		VideoList warVideos = videoFactory.getVideoList(Genre.WAR);
		warVideos.getDetails();

		VideoList warVideos1 = videoFactory.getVideoList(Genre.WAR);
		warVideos1.getDetails();

		System.out.println("Is hashCode of warVideos and warVideos1 are same ? "
				+ new Boolean(warVideos1.hashCode() == warVideos.hashCode()));
		System.out.println();

		VideoList comedyVideos = videoFactory.getVideoList(Genre.COMEDY);
		comedyVideos.getDetails();

		VideoList comedyVideos1 = videoFactory.getVideoList(Genre.COMEDY);
		comedyVideos1.getDetails();
		
		System.out.println("Is hashCode of comedyVideos and comedyVideos1 are same ? "
				+ new Boolean(comedyVideos1.hashCode() == 
                                    comedyVideos.hashCode()));
		System.out.println();

		VideoList actionVideos = videoFactory.getVideoList(Genre.ACTION);
		actionVideos.getDetails();

		VideoList actionVideos1 = videoFactory.getVideoList(Genre.ACTION);
		actionVideos1.getDetails();
		System.out.println("Is hashCode of actionVideos and actionVideos1 are same ? "
				+ new Boolean(actionVideos1.hashCode() == 
                                           actionVideos.hashCode()));
	}
}

The output of the program is as following. By comparing the hashCode() of each type of VideoList object we can see, that we get the same object every time we request for a genre.

Genre : War
##############
---------- Movie : Saving Private Ryan --------
| videoId:1
| imdbUrl:https://www.imdb.com/title/tt0120815/

---------- Movie : Enemy at the Gates --------
| videoId:13
| imdbUrl:https://www.imdb.com/title/tt0215750/

---------- Movie : Pearl Harbor --------
| videoId:27
| imdbUrl:https://www.imdb.com/title/tt0213149/

Genre : War
##############
---------- Movie : Saving Private Ryan --------
| videoId:1
| imdbUrl:https://www.imdb.com/title/tt0120815/

---------- Movie : Enemy at the Gates --------
| videoId:13
| imdbUrl:https://www.imdb.com/title/tt0215750/

---------- Movie : Pearl Harbor --------
| videoId:27
| imdbUrl:https://www.imdb.com/title/tt0213149/

Is hashCode of warVideos and warVideos1 are same ? true

Genre : Comedy
##############
---------- Movie : Toy Story --------
| videoId:25
| imdbUrl:https://www.imdb.com/title/tt0114709/

---------- Movie : The Cable Guy --------
| videoId:24
| imdbUrl:https://www.imdb.com/title/tt0115798/

Genre : Comedy
##############
---------- Movie : Toy Story --------
| videoId:25
| imdbUrl:https://www.imdb.com/title/tt0114709/

---------- Movie : The Cable Guy --------
| videoId:24
| imdbUrl:https://www.imdb.com/title/tt0115798/

Is hashCode of comedyVideos and comedyVideos1 are same ? true

Genre : Action
##############
---------- Movie : Maze Runner --------
| videoId:17
| imdbUrl:https://www.imdb.com/title/tt1790864/

---------- Movie : Black Panther --------
| videoId:34
| imdbUrl:https://www.imdb.com/title/tt1825683/

---------- Movie : Tomb Raider --------
| videoId:48
| imdbUrl:https://www.imdb.com/title/tt1365519/


Genre : Action
##############
---------- Movie : Maze Runner --------
| videoId:17
| imdbUrl:https://www.imdb.com/title/tt1790864/

---------- Movie : Black Panther --------
| videoId:34
| imdbUrl:https://www.imdb.com/title/tt1825683/

---------- Movie : Tomb Raider --------
| videoId:48
| imdbUrl:https://www.imdb.com/title/tt1365519/


Is hashCode of actionVideos and actionVideos1 are same ? true

 

core java 12

FOLLOW US ON LinkedIn



Explore Tutu'rself