You are here
Home > Design >

Structural Design Patterns In Java

Structural Design Patterns In JavaIn continuation of the first part of Design Patterns in Java, we will discuss about Structural Design Patterns In Java in this article. We have already covered Creational Design Patterns in Java in the first part as mentioned. The Structural Design Patterns deal with different ways to create a class structure. Moreover, they use Inheritance and Composition techniques to create larger objects from small objects. We will discuss seven design patterns in total in this article that come under the category of Structural Design Pattern. Let’s start discussing our topic ‘Structural Design Patterns In Java’.

What are Structural Design Patterns in Java?

Structural patterns provide different ways to create a class structure, for example, using inheritance and composition to create a large object from small objects.

What are the types of Structural Design Patterns in Java?

Following design patterns come under this category.

  1. Adapter Pattern
  2. Composite Pattern
  3. Proxy Pattern
  4. Flyweight Pattern
  5. Façade Pattern
  6. Bridge Pattern
  7. Decorator Pattern

 

Composite Design Pattern

There are times when you feel a need of a tree data structure in your code. However, there are many variations to the tree data structure, but sometimes there is a need for a tree in which both branches, as well as the leaves of the tree, should be treated uniformly.

The Composite Pattern allows you to compose objects into a tree structure to represent the part-whole hierarchy. It means you can create a tree of objects that is made of different parts, but that can be treated as a whole one big thing. Composite lets clients treat individual objects and compositions of objects uniformly, that’s the intent of the Composite Pattern.

In the composite pattern, a tree structure exists where identical operations can be performed on leaves and nodes. A node in a tree is a class that can have children. A node class is a ‘composite’ class. A leaf in a tree is a ‘primitive’ class that does not have children. The children of a composite can be leaves or other composites.

The leaf class and the composite class share a common ‘component’ interface that defines the common operations that can be performed on leaves and composites. When an operation on a composite is performed, this operation is performed on all children of the composite, whether they are leaves or composites. Hence, the composite pattern can be used to perform common operations on the objects that compose a tree.

Objects in the Composite Pattern

Base Component – Base component is the interface for all objects in the composition. Client program uses base component to work with the objects in the composition. Moreover, it can be an interface or an abstract class with some methods common to all the objects.

Leaf – Defines the behavior for the elements in the composition. It is the building block for the composition and implements base component. Although, it doesn’t have references to other Components.

Composite – It consists of leaf elements and implements the operations in base component.

Example

For example, let’s assume an organization hierarchy in terms of the Composite Pattern. Let’s assume Manager as Composite, Employee as Component, and Developer as the Leaf. First we will create component interface. It represents object in composition.  It has all common operations that will be applicable to both manager and developer.

Employee.java

/**
* Manager(Composite)
* Developer(Leaf)
* Employee(Component)
*/
public interface Employee {

      public void add(Employee emp);
      public void remove(Employee emp);
      public Employee getChild(int i);
      public String getName();
      public double getSalary();
      public void print();
}

Manager.java

Now we will create Manager (composite class). The Key point here is that all common method delegates its operations to child objects. It has method to access and modify its children.

public class Manager implements Employee {

     private String name; 
     private double salary;

     public Manager(String name, double salary) {
       this.name = name;
       this.salary = salary;
     }

     List<Employee> employees = new ArrayList<Employee>(); 

     @Override
     public void add(Employee emp) {
        employees.add(emp);
     }

     @Override
     public void remove(Employee emp) {
        employees.remove(emp);
     }

     @Override
     public Employee getChild(int i) {
        return employees.get(i);
     }

     @Override
     public String getName() {
        return name;
     }

     @Override
     public double getSalary() {
        return salary;
     }

     @Override
     public void print() {
        System.out.println("Name = " + getName());
        System.out.println("Salary = " + getSalary());
        System.out.println("-------------"); 

        Iterator<Employee> empIterator = employees.iterator();
        while(empIterator.hasNext()){
           Employee emp= empIterator.next();
           emp.print();
        }
     }
}

Developer.java

In this class, there are many methods which are not applicable to Developer because it is a leaf node.

public class Developer implements Employee {

     private String name; 
     private double salary; 

     public Developer(String name,double salary){ 
        this.name = name; 
        this.salary = salary; 
     }

     @Override
     public void add(Employee emp) {
     // this is leaf node so this method is not applicable to this class.
     }

     @Override
     public void remove(Employee emp) {
     // this is leaf node so this method is not applicable to this class.
     }

     @Override
     public Employee getChild(int i) {
        // this is leaf node so this method is not applicable to this class.
        return null;
     }

     @Override
     public String getName() {
        return name;
     }

     @Override
     public double getSalary() {
        return salary;
     }

     @Override
     public void print() {
        System.out.println("Name = " +getName()); 
        System.out.println("Salary = " +getSalary()); 
        System.out.println("-------------"); 
     }
}

CompositePatternTest.java

public class CompositePatternTest {

     public static void main(String[] args) {

     Employee emp1 = new Developer("Robert", 10000);
     Employee emp2 = new Developer("David", 15000);
     Employee manager1 = new Manager("Mark", 25000);
     manager1.add(emp1);
     manager1.add(emp2);
     Employee emp3 = new Developer("Mary", 20000);
     Manager generalManager = new Manager("John", 50000);
     generalManager.add(emp3);
     generalManager.add(manager1);
     generalManager.print();

     }
}

Output

Name =John
Salary =50000.0
-------------
Name =Mary
Salary =20000.0
-------------
Name =Mark
Salary =25000.0
-------------
Name =Robert
Salary =10000.0
-------------
Name =David
Salary =15000.0
-------------

When to use Composite Pattern

Below is the conditions when we can use this pattern:

  1. When we want to represent part-whole hierarchies of objects.
  2. When we want clients to be able to ignore the difference between compositions of objects and individual objects. Clients will treat all objects in the composite structure uniformly.

Usage in JDK

java.awt.Container#add (Component) is a great example of Composite pattern in java and used a lot in Swing.

Here, we have completed the first design pattern of our article ‘Structural Design Patterns In Java’.

 


Adapter Design Pattern

Sometimes, there could be a scenario when two objects don’t fit together, as they should in order to get the work done. For example, this situation may arise when we try to integrate a legacy code with a new code, or when changing a 3rd party API in the code. Obviously, this happens due to incompatible interfaces of the two objects which do not fit together.

As the word adapter suggests, Adapter design pattern is one of the structural design patterns that makes two unrelated interfaces work together. Moreover, the object that joins these unrelated interfaces is called an Adapter just like a mediator. As a real-life example, we can think of a mobile charger as an adapter because mobile battery needs 3 volts to charge, but the normal socket produces either 120V (in US) or 240V (in India). Therefore, the mobile charger works as an adapter between mobile charging socket and the wall socket.

In the adapter pattern, a wrapper class (i.e., the adapter) is used to translate requests from it to another class (i.e., the adoptee). In effect, an adapter provides particular interactions with an adoptee that are not offered directly by the adoptee.

The adapter pattern can take two forms : Inheritance or Composition form. In the first form, a “class adapter” utilizes inheritance. The class adapter extends the adoptee class and adds the desired methods to the adapter. These methods can be declared in an interface (i.e., the “target” interface). However, in the second form; an “object adapter” utilizes composition. The object adapter contains an adoptee and implements the target interface to interact with the adoptee.

Example 

For example, below code demonstrates the concept.

Apple.java

public class Apple {

      public void getAppleColor(String color){
         System.out.println("Apple color is : " +color);
      }
}

Orange.java

public class Orange {

      public void getOrangeColor(String color){
         System.out.println("Orange color is : " +color);
      }
}

AppleAdapter.java 

For example, Orange is adoptee, apple is target & AppleAdapter is the adapter. Here, idea is to pass adoptee in adapter’s constructor to achieve the goal.

public class AppleAdapter extends Apple {

      //The purpose of the sample problem is to adapt an orange as an apple.
      private Orange orange;

      // This is the main logic of Adapter pattern
      public AppleAdapter(Orange orange){
         this.orange = orange;
      }

      public void getColor(String color){
         orange.getOrangeColor(color);
      }
}

AdapterPatternTest.java

public class AdapterPatternTest {

       public static void main(String[] args) {

          Apple apple = new Apple();
          apple.getAppleColor("green");

          Orange orange = new Orange();
          AppleAdapter adapter = new AppleAdapter(orange);
          adapter.getAppleColor("red");
      }
}

Output

Apple color is :green
Apple color is :red

When to use Adapter Pattern

The Adapter pattern should be used when:

  1. There is an existing class, and its interface does not match the one you need.
  2. You want to create a reusable class that co-operates with unrelated or unforeseen classes, that is, classes that don’t necessarily have compatible interfaces.
  3. There are several existing subclasses to be used, but it’s impractical to adapt their interface by sub-classing each one. An object adapter can adapt the interface of its parent class.

Adapter Pattern Example in JDK

  • util.Arrays#asList()
  • io.InputStreamReader(InputStream) (returns a Reader)
  • io.OutputStreamWriter(OutputStream) (returns a Writer)

Here, we have completed the second design pattern of our article ‘Structural Design Patterns In Java’.

 


Proxy Design Pattern

The Proxy Design Pattern provides a surrogate or placeholder for another object to control access to it. In fact, the Proxy Pattern is used to create a representative object that controls access to another object. It may be remote, expensive to create or in need of being secured.

One reason for controlling access to an object is to defer the full cost of its creation and initialization until we actually need to use it. Another reason could be to act as a local representative for an object that lives in a different JVM. Moreover, the Proxy can be very useful in controlling the access to the original object, especially when objects should have different access rights.

In the Proxy Design Pattern, a client does not directly talk to the original object, it delegates calls to the proxy object which calls the methods of the original object. Moreover, the important point is that the client does not know about the proxy. The proxy acts as an original object for the client. But there are many variations to this approach which we will see shortly.

There are three main variations of the Proxy Pattern:

  1. A remote proxy provides a local representative for an object in a different address space.
  2. A virtual proxy creates expensive objects on demand.
  3. A protection proxy controls access to the original object. Protection proxies are useful when objects should have different access rights.

Example

IFolder.java

public interface IFolder {

      public void performOperations();
}

Folder.java

public class Folder implements IFolder {

      @Override
      public void performOperations() {

      // access folder and perform various operations like copy or cut files 
      System.out.println("Performing operation on folder");

      }
}

User.java

public class User {

      String userName;
      String password;

      public User(String userName, String password) {
      super();
      this.userName = userName;
      this.password = password;
      }

      public String getUserName() {
         return userName;
      }
      public void setUserName(String userName) {
         this.userName = userName;
      }
      public String getPassword() {
         return password;
      }
      public void setPassword(String password) {
         this.password = password;
      }
}

FolderProxy.java

public class FolderProxy implements IFolder {

      Folder folder;
      User user;

      public FolderProxy(User user){
         this.user=user;
      }

      @Override
      public void performOperations() {
        if (user.getUserName().equalsIgnoreCase("dev")
        && user.getPassword().equalsIgnoreCase("dev")) {
         folder = new Folder();
         folder.performOperations();
         } else {
             System.out.println("You don't have access to this folder");
         }
      } 
}

ProxyPatternTest.java

public class ProxyPatternTest {

       public static void main(String[] args) {

          User user = new User("dev", "dev");
          FolderProxy folderProxy = new FolderProxy(user);
          System.out.println("When userName and password are correct:");
          folderProxy.performOperations();
          System.out.println("------------------------------------");
          // if we give wrong userName and Password
          User userWrong = new User("abc", "abc");
          FolderProxy folderProxyWrong = new FolderProxy(userWrong);
          System.out.println("When userName and password are incorrect:");
          folderProxyWrong.performOperations();
      }
}

Output

When userName and password are correct:
Performing operation on folder
------------------------------------------------
When userName and password are incorrect:
You don't have access to this folder

When to use the Proxy Pattern

Proxy is applicable whenever there is a need for a more versatile or sophisticated reference to an object than a simple pointer. Here are several common situations in which the Proxy pattern is applicable:

  1. A remote proxy provides a local representative for an object in a different address space.
  2. A virtual proxy creates expensive objects on demand.
  3. A protection proxy controls access to the original object. Protection proxies are useful when objects should have different access rights.

Proxy Pattern in JDK

The following cases are examples of usage of the Proxy Pattern in the JDK.

  1. lang.reflect.Proxy
  2. rmi.* (whole package)

Here, we have completed the third design pattern of our article ‘Structural Design Patterns In Java’.

 


Facade Design Pattern

The Facade Design Pattern is a structural design pattern. In the facade pattern, facade classes are used to provide a single interface to set of classes. The facade simplifies a client’s interaction with a complex system by localizing the interactions into a single interface. As a result, the client can interact with a single object rather than being required to interact directly in complicated ways with the objects that make up the subsystem.

According to GoF: ‘Provide a unified interface to a set of interfaces in a subsystem. Facade Pattern defines a higher-level interface that makes the subsystem easier to use’.

However, the Facade does not encapsulate the subsystem classes or interfaces; it just provides a simplified interface to their functionality. Moreover, a client can access these classes directly. It still exposes the full functionality of the system for the clients who may need it. In a nutshell, it just provides a layer to the complex interfaces of the sub-system which makes it easier to use.

Properties of Facade Pattern

  1. Facade design pattern is one among the other design patterns that promote loose coupling. It emphasizes one most important aspect of design which is an abstraction. By hiding the complexity behind it and exposing a simple interface it achieves abstraction.
  2. Facade same as an Adapter can wrap multiple classes, but a facade uses an interface to simplify the use of the complex interface, whereas, an adapter is used to convert the interface to an interface the client expects.
  3. The mediator design pattern may look very similar to the facade design pattern in terms of abstraction. Mediator abstracts the functionality of the subsystems in this way it is similar to the facade pattern. However, in the implementation of mediator pattern, subsystem or peers’ components are aware of the mediator and that interact with it. But in case of facade pattern, subsystems are not aware of the existence of the facade. Only facade talks to the subsystems.

Example

For example, let’s create three classes named Class1, Class2, and Class3. Then, we need to simplify interaction with this system of classes so that clients can interact with these classes in a simple and regulated manner. In fact, we will do this with the Facade class.

Class1.java

The method doSomethingComplicated() takes an integer and returns its cube.

public class Class1 {

       //
      public int doSomethingComplicated(int x) {
         return x * x * x;
      }
}

Class2.java

The method doAnotherThing() doubles the cube of an integer and returns it.

public class Class2 {
       
      public int doAnotherThing(Class1 class1, int x) {
         return 2 * class1.doSomethingComplicated(x);
      }
}

Class3.java

The method doMoreStuff() takes a Class1 object, a Class2 object,and multiply them.

public class Class3 {
 
       public int doMoreStuff(Class1 class1, Class2 class2, int x) {
          return class1.doSomethingComplicated(x) * class2.doAnotherThing(class1, x);
       }
}

Facade.java

However, the names of the methods as shown below clearly indicate what they do. Furthermore, these methods hide the interactions of Class1, Class2, and Class3 from client code.

public class Facade {

      public int cubeX(int x) {
         Class1 class1 = new Class1();
         return class1.doSomethingComplicated(x);
      }

      public int cubeXTimes2(int x) {
         Class1 class1 = new Class1();
         Class2 class2 = new Class2();
         return class2.doAnotherThing(class1, x);
      }

      public int multiplyBoth(int x) {
         Class1 class1 = new Class1();
         Class2 class2 = new Class2();
         Class3 class3 = new Class3();
         return class3.doMoreStuff(class1, class2, x);
      }
}

FacadePatterntest.java

public class FacadePatternTest {

      public static void main(String[] args) {

         Facade facade = new Facade();
         int x = 3;
         System.out.println("Cube of " + x + ":" + facade.cubeX(3));
         System.out.println("Cube of " + x + " times 2:" + facade.cubeXTimes2(3));
         System.out.println(x + " multiply class1 & class2 :" + facade.multiplyBoth(3));
      }
}

Output

Cube of 3:27
Cube of 3 times 2:54
3 multiply class1 & class2 :1458

When to use 

  • Facade pattern is more like a helper for client applications; it doesn’t hide subsystem interfaces from the client. Whether to use Facade or not is completely dependent on client code.
  • Facade pattern can be applied at any point of development, usually when the number of interfaces grows and system gets complex.
  • Subsystem interfaces are not aware of Facade and they shouldn’t have any reference of the Facade interface.
  • A facade pattern should be applied for similar kind of interfaces; its purpose is to provide a single interface rather than multiple interfaces that does the similar kind of jobs.
  • The subsystem may be depended with one another. In such case, facade can act as a coordinator and decouple the dependencies between the subsystems.
  • We can use the Factory pattern with Facade to provide a better interface to client systems.

Usage in Java

In javax.faces.context, ExternalContext internally uses ServletContext, HttpSession, HttpServletRequest, HttpServletResponse, etc. It allows the Faces API to be unaware of the nature of its containing application environment.

Here, we have completed the fourth design pattern of our article ‘Structural Design Patterns In Java’.

 


Bridge Design Pattern

When we have interface hierarchies in both interfaces as well as implementations, then the BRIDGE design pattern is used to decouple the interfaces from implementation and hiding the implementation details from the client programs.

According to the GoF bridge design pattern is: Decouple an abstraction from its implementation so that the two can vary independently.

In the Bridge Pattern, we separate an abstraction and its implementation and develop separate inheritance structures for both the abstraction and the implementer. The abstraction is an interface or an abstract class, and likewise the implementer is an interface or an abstract class. The abstraction contains a reference to the implementer. Children of the abstraction are referred to as refined abstractions, and children of the implementer are concrete implementers. Since we can change the reference to the implementer in the abstraction, we are able to change the abstraction’s implementer at run-time. However, changes to the implementer do not affect client code.

The Bridge Pattern’s intent is to put the abstraction and implementation into two different class hierarchies so that both can be extend independently.

Adapter Pattern vs Bridge Pattern

The Adapter Design Pattern helps it two incompatible classes to work together. But the Bridge Design Pattern decouples the abstraction and implementation by creating two different hierarchies.

Example

For example, let’s observe the code below in order to understand the Bridge Design Pattern. Before that, just look at the diagram below to make everything clear.

Bridge Design Pattern

Vehicle.java

/**
* abstraction in Bridge pattern
* */
public abstract class Vehicle {
      protected VehicleType type1;
      protected VehicleType type2;

      public Vehicle(VehicleType type1, VehicleType type2) {
         this.type1 = type1;
         this.type2 = type2;
      }

      abstract public void purchase();
}

VehicleType.java

/**
* Implementor for Bridge pattern
* */
public interface VehicleType {

      abstract public void book();
}

Car.java

/**
* Refine abstraction 1 in Bridge pattern
*/
public class Car extends Vehicle {

       public Car(VehicleType type1, VehicleType type2) {
          super(type1, type2);
       }

       @Override
       public void purchase() {
          System.out.print("Car");
          type1.book();
          type2.book();
       }
}

Bike.java

/**
* Refine abstraction 2 in Bridge pattern
*/
public class Bike extends Vehicle {

      public Bike(VehicleType type1, VehicleType type2) {
         super(type1, type2);
      }

      @Override
      public void purchase() {
         System.out.print("Bike");
         type1.book();
         type2.book();
      }
}

NewVehicle.java

/**
* Concrete implementation 1 for Bridge pattern
* */
public class NewVehicle implements VehicleType {

       @Override
       public void book() {
          System.out.print(" : New Vehicle");
       }
}

OldVehicle.java

/**
* Concrete implementation 2 for Bridge pattern
* */
public class OldVehicle implements VehicleType {

      @Override
      public void book() {
         System.out.println(" : Old Vehicle");
      }
}

BridgePatternTest.java

public class BridgePatternTest {

      public static void main(String[] args) {

         Vehicle vehicle1= new Car(new NewVehicle(),new OldVehicle());
         vehicle1.purchase();

         Bike vehicle2 = new Bike(new NewVehicle(),new OldVehicle());
         vehicle2.purchase();
      }
}

Output

Car : New Vehicle : Old Vehicle
Bike : New Vehicle : Old Vehicle

When to Use and other points

  • Should be used when we have a need to switch implementation at runtime.
  • The client should not be impacted if there is a modification in implementation of abstraction.
  • Best used when you have multiple implementations.
  • Creates two different hierarchies. One for abstraction and another for implementation.
  • Avoids permanent binding by removing the dependency between abstraction and implementation.
  • We create a bridge that coordinates between abstraction and implementation.
  • Abstraction and implementation can be extended separately.

Usage in JDK

  • AWT (It provides an abstraction layer which maps onto the native OS the windowing support.)
  • JDBC

Here, we have completed the fifth design pattern of our article ‘Structural Design Patterns In Java’.

 


Decorator Design Pattern

The primary intent of the Decorator Design Pattern is to attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to sub-classing for extending functionality.

The Decorator Pattern facilitates us when we need to extend the functionality of an object dynamically without having to change the original class source or using inheritance. This is accomplished by creating an object wrapper referred to as a Decorator around the actual object.

The Decorator object has the same interface as the underlying object. This allows a client object to interact with the Decorator object in exactly the same manner as it would with the underlying actual object. The Decorator object contains a reference to the actual object. The Decorator object receives all requests (calls) from a client. In turn, it forwards these calls to the underlying object. The Decorator object adds some additional functionality before or after forwarding requests to the underlying object. This ensures that the additional functionality can be added to a given object externally at runtime without modifying its structure.

We use inheritance or composition to extend the behavior of an object, but this is done at compile time and it’s applicable to all the instances of the class. We can’t add any new functionality or remove any existing behavior at runtime – this is when the Decorator pattern comes into the picture.

Example

For example, let’s assume that we have to decorate the Ice cream programmatically. We will learn how the Decorator Pattern works for this example along with other related terms.

Component Interface 

The interface or abstract class defining the methods that will be implemented. For example, in our case Icecream will be the component interface as below.

public interface Icecream {

      public String makeIcecream();
}

Component Implementation 

The basic implementation of the component interface. For example, we can have SimpleIcecream class as our component implementation as below.

public class SimpleIcecream implements Icecream {

      @Override
      public String makeIcecream() {

          return "Base-Icecream ";
      }
}

Decorator

The decorator class implements the component interface and it has a HAS-A relationship with the component interface. The component variable should be accessible to the child decorator classes, so we will make this variable protected. For example, below IcecreamDecorator class will work as Decorator in our case.

public class IcecreamDecorator implements Icecream {

       protected Icecream specialIcecream;

       public IcecreamDecorator(Icecream specialIcecream){
          this.specialIcecream = specialIcecream;
       }

       @Override
       public String makeIcecream() {
          return specialIcecream.makeIcecream();
      }
}

Concrete Decorators

Extending the base decorator functionality and modifying the component behavior accordingly. For example, we can have concrete decorator classes like NuttyIcecreamDecorator & HoneyIcecreamDecorator as below.

NuttyDecorator.java

public class NuttyDecorator extends IcecreamDecorator {

       public NuttyDecorator(Icecream specialIcecream) {
          super(specialIcecream);
       }

       public String makeIcecream(){
          return specialIcecream.makeIcecream() + addNuts();
       }

       private String addNuts() {
          return "+ Crunchy Nuts ";
       }
}

HoneyDecorator.java

public class HoneyDecorator extends IcecreamDecorator {

      public HoneyDecorator(Icecream specialIcecream) {
         super(specialIcecream);
      }

      public String makeIcecream(){
         return specialIcecream.makeIcecream() + addHoney();
      }

      private String addHoney() {
         return "+ Sweet Honey ";
      }
}

DecoratorPatternTest.java

public class DecoratorPatternTest {

       public static void main(String[] args) {

          Icecream icecream = new HoneyDecorator(new NuttyDecorator(new SimpleIcecream()));
          System.out.println(icecream.makeIcecream());
       }
}

Output

Base-Icecream + Crunchy Nuts + Sweet Honey 

When to use the Decorator Design Pattern

Use the Decorator pattern in the following cases:

  • To add responsibilities to individual objects dynamically and transparently, that is, without affecting other objects.
  • For responsibilities that can be withdrawn.
  • When extension by sub-classing is impractical. Sometimes a large number of independent extensions are possible and would produce an explosion of subclasses to support every combination. Or a class definition may be hidden or otherwise unavailable for sub-classing.
  • It’s easy to maintain and extend when the number of choices are more.

Usage in Java

We use the decorator pattern mostly in Java IO classes, such as FileReader, BufferedReader etc.

  • The disadvantage of decorator pattern is that it uses a lot of similar kind of objects (decorators).

Here, we have completed the sixth design pattern of our article ‘Structural Design Patterns In Java’.

 


Structural Design Patterns In JavaFlyweight Design Pattern

In the flyweight pattern, instead of creating large numbers of similar objects, we re-use objects. We can use it to reduce memory requirements and instantiation time and related costs.

Before we apply the flyweight design pattern, we need to consider the following factors:

  • If the number of Objects required in the application are huge in amount.
  • Also, if the object creation is heavy on memory and it can also be time consuming.

Many a time, too many objects can slow the application performance down. Obviously, too many objects might consume a larger piece of memory. Also, they can slow down the application or even may cause out of memory problems. However, a good programmer always  keeps track of the object’s creation and controls it in the application. This is especially true, when we have a lot of similar objects and two objects from the pool don’t have many 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, the application developer should control them. 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.

When to Use

Flyweight design pattern facilitates us when we need to create a lot of Objects of a class. Since every object consumes memory space, it can play a crucial role for low memory devices, such as mobile devices or embedded systems. Moreover, we can apply flyweight design pattern in order to reduce the load on memory with the help of object’s sharing.

Flyweight Pattern Example in JDK

All the wrapper classes valueOf () method uses cached objects showing use of Flyweight design pattern. The best example is Java String class String Pool implementation.

Here, we have completed the seventh design pattern of our article ‘Structural Design Patterns In Java’.

Links to Other Design Patterns 

Creational Design Patterns

Behavioral Design Patterns


 

One thought on “Structural Design Patterns In Java

Leave a Reply


Top