Yes, you guessed it right. The main advantage of using Flyweight Design Pattern is to reduce object creation and reuse the existing ones. As described in the book Design Patterns: Elements of Reusable Object-Oriented Software by Erich Gamma et al. ( Addison-Wesley, 1995 ) at page 195 (Also called GoF: Gang of Four): “Use sharing to support large numbers of fine grained objects efficiently”.
The technology stack for the previous project was JSF, Tiles and Hibernate. We used POJO for our Manager classes (Service classes) and DAO classes. A project can have many Manager classes and DAO Classes and let say the application is meant for 260 – 340 users with 2 GB of RAM allocated to your application on the server. In such situations you need to design your application in a way that it uses memory optimally. Talking about memory optimization in your application, Flyweight Pattern is the ideal choice. In this article I will demonstrate the usage of this pattern. One important point that you need to rememeber is that objects that you intend to share within multiple threads/requests should be thread-safe, otherwise you will face consistency issues.
package com.test.factory;
import java.util.Hashtable;
public class ManagerFactory {
/**
* Contains references of Manager that has been instantiated.
*/
private Hashtable managerReferenceTable = new Hashtable();
/**
* Contains the reference of ManagerFactory
*/
private static ManagerFactory facadeInstance;
/**
* Private Constructor.
*
*/
private ManagerFactory() {
}
/**
* Returns the instance of ManagerFactory
*
* @return
*/
public static synchronized ManagerFactory getInstance() {
if (facadeInstance == null) {
facadeInstance = new ManagerFactory();
}
return facadeInstance;
}
/**
* This method returns the reference of a Manager. It checks if it is
* already created and returns the same reference otherwise it create a new
* object of the Manager and returns the newly created object.
*
* @param managerClazz
* @return
*/
private Manager instantiateManager(Class managerClazz) {
Manager manager;
String clazzName = managerClazz.getName();
// check if manager already instantiated earlier.
if ((manager = (Manager) managerReferenceTable.get(clazzName)) != null) {
return manager;
} else {
// if not created earlier then instantiate a new object of the
// manager and store the reference in the “managerReferenceTable”
try {
manager = (Manager) managerClazz.newInstance();
managerReferenceTable.put(clazzName, manager);
return manager;
} catch (Exception ex) {
// throw exception if not able to instantiate.
throw new IllegalStateException(“Unable to create instance of “
+ managerClazz + ” Manager Class”);
}
}
}
/**
* Returns instance of OrderManager
*
* @return
*/
public OrderManager getReferenceDataManager() {
return (OrderManager) instantiateManager(OrderManagerImpl.class);
}
/**
* Returns instance of InventoryManager
*
* @return
*/
public InventoryManager getInventoryManager() {
return (InventoryManager) instantiateManager(InventoryManagerImpl.class);
}
}
| Java | | copy code | | ? |
| 01 | package com.test.factory; |
| 02 | |
| 03 | import java.util.Hashtable; |
| 04 | |
| 05 | public class ManagerFactory { |
| 06 | |
| 07 | /** |
| 08 | * Contains references of Manager that has been instantiated. |
| 09 | */ |
| 10 | private Hashtable managerReferenceTable = new Hashtable(); |
| 11 | |
| 12 | /** |
| 13 | * Contains the reference of ManagerFactory |
| 14 | */ |
| 15 | private static ManagerFactory facadeInstance; |
| 16 | |
| 17 | /** |
| 18 | * Private Constructor. |
| 19 | * |
| 20 | */ |
| 21 | private ManagerFactory() { |
| 22 | |
| 23 | } |
| 24 | |
| 25 | /** |
| 26 | * Returns the instance of ManagerFactory |
| 27 | * |
| 28 | * @return |
| 29 | */ |
| 30 | public static synchronized ManagerFactory getInstance() { |
| 31 | |
| 32 | if (facadeInstance == null) { |
| 33 | facadeInstance = new ManagerFactory(); |
| 34 | } |
| 35 | return facadeInstance; |
| 36 | } |
| 37 | |
| 38 | /** |
| 39 | * This method returns the reference of a Manager. It checks if it is |
| 40 | * already created and returns the same reference otherwise it create a new |
| 41 | * object of the Manager and returns the newly created object. |
| 42 | * |
| 43 | * @param managerClazz |
| 44 | * @return |
| 45 | */ |
| 46 | private Manager instantiateManager(Class managerClazz) { |
| 47 | |
| 48 | Manager manager; |
| 49 | String clazzName = managerClazz.getName(); |
| 50 | |
| 51 | // check if manager already instantiated earlier. |
| 52 | if ((manager = (Manager) managerReferenceTable.get(clazzName)) != null) { |
| 53 | return manager; |
| 54 | } else { |
| 55 | // if not created earlier then instantiate a new object of the |
| 56 | // manager and store the reference in the "managerReferenceTable" |
| 57 | |
| 58 | try { |
| 59 | manager = (Manager) managerClazz.newInstance(); |
| 60 | managerReferenceTable.put(clazzName, manager); |
| 61 | return manager; |
| 62 | } catch (Exception ex) { |
| 63 | // throw exception if not able to instantiate. |
| 64 | throw new IllegalStateException("Unable to create instance of " |
| 65 | + managerClazz + " Manager Class"); |
| 66 | } |
| 67 | } |
| 68 | } |
| 69 | |
| 70 | /** |
| 71 | * Returns instance of OrderManager |
| 72 | * |
| 73 | * @return |
| 74 | */ |
| 75 | public OrderManager getOrderManager() { |
| 76 | return (OrderManager) instantiateManager(OrderManagerImpl.class); |
| 77 | } |
| 78 | |
| 79 | /** |
| 80 | * Returns instance of InventoryManager |
| 81 | * |
| 82 | * @return |
| 83 | */ |
| 84 | public InventoryManager getInventoryManager() { |
| 85 | return (InventoryManager) instantiateManager(InventoryManagerImpl.class); |
| 86 | } |
| 87 | } |
Now the above code demonstrates how Flyweight pattern is implemented. In this implementation of Factory pattern, it maintains a Hashtable. I used Hashtable intentionally because it is synchronized. Initially this table will be empty but gradually will contain different Manager objects, one object for a type. Whenever a consumer requests for a Manager object of type OrderManager, this Factory implementation will check whether any instance of the same type has already been created and if it already exists in the Hashtable it will return the manager object. In case it is not able to find one then the Factory will create a new instance and save it in its Hashtable instance with fully qualified name as Key and object created as Value.
The direct benefit of using this pattern is that even during the peak time this will create only single instance per type that you have defined your Factory class to cater. This code has been tested under different circumstances and successfully running in production.
You can further improve the above code for performance as per your requirements. Like you can use WeakHashMap instead of Hashtable so that manager objects that are not being used can be garbage-collected. You can also synchronize code to further than the code above.
More synchronized code
| Java | | copy code | | ? |
| 01 | package com.test.factory; |
| 02 | |
| 03 | import java.util.Hashtable; |
| 04 | |
| 05 | public class ManagerFactory { |
| 06 | |
| 07 | /** |
| 08 | * Contains references of Manager that has been instantiated. |
| 09 | */ |
| 10 | private Hashtable managerReferenceTable = new Hashtable(); |
| 11 | |
| 12 | /** |
| 13 | * Contains the reference of ManagerFactory |
| 14 | */ |
| 15 | private static ManagerFactory facadeInstance; |
| 16 | |
| 17 | /** |
| 18 | * Private Constructor. |
| 19 | * |
| 20 | */ |
| 21 | private ManagerFactory() { |
| 22 | |
| 23 | } |
| 24 | |
| 25 | /** |
| 26 | * Returns the instance of ManagerFactory |
| 27 | * |
| 28 | * @return |
| 29 | */ |
| 30 | public static synchronized ManagerFactory getInstance() { |
| 31 | |
| 32 | if (facadeInstance == null) { |
| 33 | facadeInstance = new ManagerFactory(); |
| 34 | } |
| 35 | return facadeInstance; |
| 36 | } |
| 37 | |
| 38 | /** |
| 39 | * This method returns the reference of a Manager. It checks if it is |
| 40 | * already created and returns the same reference otherwise it create a new |
| 41 | * object of the Manager and returns the newly created object. |
| 42 | * |
| 43 | * @param managerClazz |
| 44 | * @return |
| 45 | */ |
| 46 | private Manager instantiateManager(Class managerClazz) { |
| 47 | |
| 48 | Manager manager; |
| 49 | String clazzName = managerClazz.getName(); |
| 50 | |
| 51 | // check if manager already instantiated earlier. |
| 52 | if ((manager = (Manager) managerReferenceTable.get(clazzName)) != null) { |
| 53 | return manager; |
| 54 | } else { |
| 55 | // if not created earlier then instantiate a new object of the |
| 56 | // manager and store the reference in the "managerReferenceTable" |
| 57 | synchronized (this) { |
| 58 | if ((manager = (Manager) managerReferenceTable.get(clazzName)) == null) { |
| 59 | try { |
| 60 | manager = (Manager) managerClazz.newInstance(); |
| 61 | managerReferenceTable.put(clazzName, manager); |
| 62 | return manager; |
| 63 | } catch (Exception ex) { |
| 64 | // throw exception if not able to instantiate. |
| 65 | throw new IllegalStateException( |
| 66 | "Unable to create instance of " + managerClazz + " Manager Class"); |
| 67 | } |
| 68 | |
| 69 | } else { |
| 70 | return manager; |
| 71 | } |
| 72 | |
| 73 | } |
| 74 | |
| 75 | } |
| 76 | |
| 77 | } |
| 78 | |
| 79 | /** |
| 80 | * Returns instance of OrderManager |
| 81 | * |
| 82 | * @return |
| 83 | */ |
| 84 | public OrderManager getOrderManager() { |
| 85 | return (OrderManager) instantiateManager(OrderManagerImpl.class); |
| 86 | } |
| 87 | |
| 88 | /** |
| 89 | * Returns instance of InventoryManager |
| 90 | * |
| 91 | * @return |
| 92 | */ |
| 93 | public InventoryManager getInventoryManager() { |
| 94 | return (InventoryManager) instantiateManager(InventoryManagerImpl.class); |
| 95 | } |
| 96 | } |
#1 by scottyab on August 13th, 2009