Flyweight Pattern: Developers' Art of Reusing


An Example of Programmer
Imagine we define a standard for programmers via a Programmer
interface. Its attributes are the programmer’s skills, and it has a method called code()
. Now, whenever a client needs to develop a system, the company simply hires (i.e., new
) a programmer with the right skill set, let them code, and just like that—the system is delivered. But of course, hiring a new programmer for every project and then letting them go afterward is costly. (Similarly, in the JVM, creating and garbage collecting objects isn’t free either. Why GC comes with a cost is another story for another day.)
interface Programmer{
void code(String requirement);
}
class BackendDeveloper implements Programmer{
void code(String requirement){
System.out.println("I can develop a backend system");
}
}
class FrontDeveloper implements Programmer{
void code(String requirement){
System.out.println("I can develop a backend system");
}
}
class Company{
public static void main(String[] args) {
// Project A: develop a new backend system A
Programmer p1 = new BackendDeveloper();
p1.code("projectA");
// Project B: develop a another backend system B
Programmer p2 = new BackendDeveloper();
p2.code("projectB");
// Project C: develop a another frontend system C
Programmer p3 = new FrontDeveloper();
p3.code("projectC");
}
}
To cut costs, companies often look for smarter approaches. Instead of hiring a brand-new programmer for every new project or system, they’ll first check whether there’s already someone in-house with the right skill set (say, a backend or frontend developer). In reality, A single type of developer can handle multiple projects. So if the company already has the right kind of programmer, they just reuse them. This saves a lot of money—and that’s essentially what the Flyweight Pattern does.
Flyweight pattern
Now, back to the Flyweight Pattern. From the name alone, it’s about sharing instances to keep things as lightweight as possible. From this perspective, the pattern boils down to two key ideas:
Separate what changes from what doesn’t (external vs. internal state).
Use a factory to manage the shared (unchanging) objects, while passing in the varying parts as parameters.
In practice, a Flyweight setup usually involves:
Flyweight (interface): Defines the common behavior (e.g., every programmer must be able to
code()
);ConcreteFlyweight: Multiple reusable implementations (frontend, backend, Java dev, Python dev, etc.). (Note: Flyweight is not the same as an object pool.)
FlyweightFactory: Returns the right instance based on the request (e.g., provide a specific programmer type depending on the system being built).
The essence of Flyweight design is to decouple what varies from what stays constant. A programmer’s core skill set doesn’t usually change; what changes are the specific project requirements. By reusing the same type of programmer object, we achieve efficiency.
class Factory{
Map<String, Programmer> map = new HashMap<>();
public Programmer getDeveloper(String type){
if(!map.containsKey(type)){
if(type.equals("backend")){
map.put("backend", new BackendDeveloper());
}else if(type.equals("frontend")){
map.put("frontend", new FrontDeveloper());
}
return map.get("backend");
}
}
}
class Company{
public static void main(String[] args) {
// Project A: develop a new backend system A
Factory factory = new Factory();
Programmer p1 = factory.getDeveloper("backend");
p1.code("projectA");
// Project B: develop a another backend system B
Programmer p2 = factory.getDeveloper("backend");
p2.code("projectB");
// Project C: develop a another frontend system C
Programmer p3 = factory.getDeveloper("frontend");
p3.code("projectC");
}
}
Here’s the Flyweight structure:
Subscribe to my newsletter
Read articles from ukelili directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
