Core Java

Flyweight Design Pattern Example

This article is part of our Academy Course titled Java Design Patterns.

In this course you will delve into a vast number of Design Patterns and see how those are implemented and utilized in Java. You will understand the reasons why patterns are so important and learn when and how to apply each one of them. Check it out here!

1. Flyweight Pattern

Object Oriented programming has made programming easy and interesting. It makes a programmer’s job easier by modeling real world entities into the programming world. A programmer creates a class and instantiates it by creating an object of it. This object models a real world entity and objects inside an application coordinate with each other in order to accomplish the required work.

But sometimes too many objects can slow things down. Too many objects might consume a large piece of memory and can slow down the application or even cause out of memory problems. As a good programmer, one should keep track of instantiated objects and control the object creation in an application. This is especially true, when we have a lot of similar objects and two objects from the pool don’t have much differences between them.

Sometimes the objects in an application might have great similarities and be of a similar kind (a similar kind here means that most of their properties have similar values and only a few of them vary in value). In case they are also heavy objects to create, they should be controlled by the application developer. Otherwise, they might consume much of the memory and eventually slow down the whole application.

The Flyweight Pattern is designed to control such kind of object creation and provides you with a basic caching mechanism. It allows you to create one object per type (the type here differs by a property of that object), and if you ask for an object with the same property (already created), it will return you the same object instead of creating a new one.

Before digging into the details of the Flyweight pattern, let’s consider the following scenario: a site which allows users to create and execute programs online. We will discuss the scenario now and we will later try to solve the problem using the Flyweight pattern.

The X-programming site allows users to create and execute programs using their favorite programming language. It provides you with plenty of programming language options. You choose one, write a program with it and execute it to see the result.

But now the site has started losing its users, the reason being the slowness of the site. The users are not interested in it any more. The site is very popular and sometimes there could be more than thousands of programmers using it. Because of that, the site is crawling. But the heavy usage is not the real problem behind the slowness of the site. Let us see the core programming of the site which allows users to run and execute their program, and the true issue will be revealed there.

package com.javacodegeeks.patterns.flyweightpattern;

public class Code {
	
	private String code;

	public String getCode() {
		return code;
	}

	public void setCode(String code) {
		this.code = code;
	}
	
}

The above class is used to set the code done by the programmer in order to get it executed. The Code object is a lightweight simple object having a property code along with its setter and getter.

package com.javacodegeeks.patterns.flyweightpattern;

public interface Platform {

	public void execute(Code code);
}

The Platform interface is implemented by the language specific platform in order to execute the code. It has one method, executes, which takes the Code object as its parameter.

package com.javacodegeeks.patterns.flyweightpattern;

public class JavaPlatform implements Platform {

	public JavaPlatform(){
		System.out.println("JavaPlatform object created");
	}
	
	@Override
	public void execute(Code code) {
		System.out.println("Compiling and executing Java code.");
	}

}

The above class implements the Platform interface and provides an implementation for the execute method, to execute the code in Java.

To execute the code, a Code object which contains the code and a Platform object to execute the code get created. The code looks like this:

Platform platform = new JavaPlatform();
platform.execute(code);

Now suppose, there are around 2k users online and executing their code which results in 2k Code objects and 2k Platform objects. The Code object is a light weighted object and there should also be one Code object per user code. But, the Platform is a heavy object which is used to set the execution environment. Creating too many Platform objects is time consuming, and a heavy task. We need to control the creation of the Platform object which can be done using the Flyweight Pattern, but before that, let’s look at the detail of the Flyweight Pattern.

2. What is the Flyweight Pattern

The intent of the Flyweight Pattern is to use shared objects to support large numbers of fine-grained objects efficiently. A flyweight is a shared object that can be used in multiple contexts simultaneously. The flyweight acts as an independent object in each context – it’s indistinguishable from an instance of the object that’s not shared. Flyweights cannot make assumptions about the context in which they operate. The key concept here is the distinction between intrinsic and extrinsic state. Intrinsic state is stored in the flyweight; it consists of information that’s independent of the flyweight’s context, thereby making it sharable. The extrinsic state depends on and varies with the flyweight’s context and therefore can’t be shared. Client objects are responsible for passing extrinsic state to the flyweight when it needs it.

Consider an application scenario that involves creating a large number of objects that are unique only in terms of a few parameters. In other words, these objects contain some intrinsic, invariant data that are common among all objects. This intrinsic data needs to be created and maintained as part of every object that is being created. The overall creation and maintenance of a large group of such objects can be very expensive in terms of memory-usage and performance. The Flyweight pattern can be used in such scenarios to design a more efficient way of creating objects.

Here is the class diagram for the Flyweight design pattern:

Figure 1
Figure 1

Flyweight

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

ConcreteFlyweight

  • Implements the Flyweight interface and adds storage for intrinsic state, if any. A ConcreteFlyweight object must be sharable. Any state it stores must be intrinsic; that is, it must be independent of the ConcreteFlyweight object’s context.

FlyweightFactory

  • Creates and manages flyweight objects.
  • Ensures that flyweights are shared properly. When a client requests a flyweight, the FlyweightFactory object supplies an existing instance or creates one, if none exists.

Client

  • Maintains a reference to flyweight(s).
  • Computes or stores the extrinsic state of flyweight(s).

3. Solution to the Problem

To solve the above problem, we will provide a platform factory class which will control the creation of the Platform objects.

package com.javacodegeeks.patterns.flyweightpattern;

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

public final class PlatformFactory {
	
	private static Map<String, Platform> map = new HashMap<>();
	private PlatformFactory(){
		throw new AssertionError("Cannot instantiate the class");
	}
	
	public static synchronized Platform getPlatformInstance(String platformType){
		Platform platform = map.get(platformType);
		if(platform==null){
			switch(platformType){
				case "C" : platform = new CPlatform(); 
						   break;
				case "CPP" : platform = new CPPPlatform(); 
				   		   break;
				case "JAVA" : platform = new JavaPlatform(); 
				   		   break;
				case "RUBY" : platform = new RubyPlatform(); 
				   		   break;   		   
			}
			map.put(platformType, platform);
		}
		return platform;
	}

}

The above class contains a static map which holds a String object as key and a Platform type object as its value. We don’t want to create the instance of this class so just kept its constructor private and throw an AssertionError just to avoid any accidental creation of the object even within the class.

The main and the only method of this class is the getPlatformInstance method. This is a static method which has a platformType as its parameter. This platformType is used as the key in the map, it first checks the map whether a platform object having the key is already exists or not. If no object found, the appropriate platform object gets created, it is put into the map and then the method returns the object. Next time, when the same platform type object is requested, the same existing object is returned, instead of a new object.

Also, please note that the getPlatformInstance method is synchronized in order to provide the thread safety while checking and creating the instance of the object. In the above example, there isn’t any intrinsic property of the object that is shared, but only the extrinsic property which is the code object provided by the client code.

Now, let’s test the code.

package com.javacodegeeks.patterns.flyweightpattern;

public class TestFlyweight {

	public static void main(String[] args) {

		Code code = new Code();
		code.setCode("C Code...");
		Platform platform = PlatformFactory.getPlatformInstance("C");
		platform.execute(code);
		System.out.println("-------------------------------------");
		code = new Code();
		code.setCode("C Code2...");
		platform = PlatformFactory.getPlatformInstance("C");
		platform.execute(code);
		System.out.println("-------------------------------------");
		code = new Code();
		code.setCode("JAVA Code...");
		platform = PlatformFactory.getPlatformInstance("JAVA");
		platform.execute(code);
		System.out.println("-------------------------------------");
		code = new Code();
		code.setCode("JAVA Code2...");
		platform = PlatformFactory.getPlatformInstance("JAVA");
		platform.execute(code);
		System.out.println("-------------------------------------");
		code = new Code();
		code.setCode("RUBY Code...");
		platform = PlatformFactory.getPlatformInstance("RUBY");
		platform.execute(code);
		System.out.println("-------------------------------------");
		code = new Code();
		code.setCode("RUBY Code2...");
		platform = PlatformFactory.getPlatformInstance("RUBY");
		platform.execute(code);
	}

}

The above code will result into the following output:

CPlatform object created
Compiling and executing C code.
-------------------------------------
Compiling and executing C code.
-------------------------------------
JavaPlatform object created
Compiling and executing Java code.
-------------------------------------
Compiling and executing Java code.
-------------------------------------
RubyPlatform object created
Compiling and executing Ruby code.
-------------------------------------
Compiling and executing Ruby code.

In the above class, we have first created a Code object and set the C code in it. Then, we asked the PlatformFactory to provide C platform to execute the code. Later, we called the execute method on the object returned, bypassing the Code object to it.


 
We performed the same procedure, i.e. creating and setting the Code object, and then asking for a platform object, specific to the code. The output clearly shows that the platform objects created only the first time they are requested for; on the next attempts the same object gets returned.

The other platform specific classes are similar to the JavaPlatform class, already shown.

package com.javacodegeeks.patterns.flyweightpattern;

public class CPlatform implements Platform {
	
	public CPlatform(){
		System.out.println("CPlatform object created");
	}

	@Override
	public void execute(Code code) {
		System.out.println("Compiling and executing C code.");
	}

}

package com.javacodegeeks.patterns.flyweightpattern;

public class CPPPlatform implements Platform{
	
	public CPPPlatform(){
		System.out.println("CPPPlatform object created");
	}
	
	@Override
	public void execute(Code code) {
		System.out.println("Compiling and executing CPP code.");
	}

}

package com.javacodegeeks.patterns.flyweightpattern;

public class RubyPlatform implements Platform{
	
	public RubyPlatform(){
		System.out.println("RubyPlatform object created");
	}
	
	@Override
	public void execute(Code code) {
		System.out.println("Compiling and executing Ruby code.");
	}

}

If we reconsider that 2k users concurrently use the site, exactly 2k light weight Code object get created, and only 4 heavy weight platform objects are instantiated. Please note that, we are saying 4 platform objects by considering that there would be at least one user per language. If, for example, let say no user have coded using the Ruby, only 3 platform objects get created in that scenario.

4. When to use the Flyweight Pattern

The Flyweight pattern’s effectiveness depends heavily on how and where it’s used. Apply the Flyweight pattern when all of the following are true:

  • An application uses a large number of objects.
  • Storage costs are high because of the sheer quantity of objects.
  • Most object state can be made extrinsic.
  • Many groups of objects may be replaced by relatively few shared objects once extrinsic state is removed.
  • The application doesn’t depend on object identity. Since flyweight objects may be shared, identity tests will return true for conceptually distinct objects.

5. Flyweight in the JDK

The following are the usage(s) of the Flyweight Pattern in JDK.

  • java.lang.Integer#valueOf(int) (also on Boolean, Byte, Character, Short and Long)

6. Download the Source Code

This was a lesson on the Flyweight Pattern. You may download the source code here: Flyweight Pattern Project

Rohit Joshi

Rohit Joshi is a Senior Software Engineer from India. He is a Sun Certified Java Programmer and has worked on projects related to different domains. His expertise is in Core Java and J2EE technologies, but he also has good experience with front-end technologies like Javascript, JQuery, HTML5, and JQWidgets.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

2 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Anant Kame
Anant Kame
7 years ago

best explanation provided ,with suitable real time examples.nice artical.

Vinay Kumar
Vinay Kumar
6 years ago

Can we use this pattern for db connection?

Back to top button