Fork me on GitHub
Tutorial Home

Flyweight

Story

Remove duplicates.

Flyweight pattern is used to reduce memory by loading only the data necessary to perform action. Database normalization is flyweight. Normalisation, is the process of organizing the columns (attributes) and tables (relations) of a relational database to minimize data redundancy.

Image

alt text

By 04hutts (Own work) [Public domain], via Wikimedia Commons

UML

Implementation

Flyweight.java

package com.hundredwordsgof.flyweight;

/**
 * 
 * Flyweight, interface through flyweight can receive and act on extrinsic
 * state.
 *
 */
public interface Flyweight {

  void operation(Object extrinsicState);
}

ConcreteFlyweight.java

package com.hundredwordsgof.flyweight;

/**
 * 
 * ConcreteFlyweight,implements Flyweight, and add storage for intrisnic state.
 *
 */
public class ConcreteFlyweight implements Flyweight {

  private Object intrinsicState;

  public ConcreteFlyweight(Object intrinsicState) {
    this.intrinsicState = intrinsicState;
  }

  // Using extrinsicState as context and does NOT modify intrinsic state.
  public void operation(Object extrinsicState) {
  }

  /**
   * @return intrinsic state
   */
  public Object getIntrinsicState() {
    return intrinsicState;
  }
}

UnsharedConcreteFlyweight.java

package com.hundredwordsgof.flyweight;

/**
 * UnsharedConcreteFlyweight, defines objects which are not shared.
 *
 */
public class UnsharedConcreteFlyweight implements Flyweight {

  private Object state;

  public UnsharedConcreteFlyweight(Object state) {
    this.state = state;
  }

  public void operation(Object extrinsicState) {

  }

  public Object getState() {
    return state;
  }
}

FlyweightFactory.java

package com.hundredwordsgof.flyweight;

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

/**
 * 
 * FlyweightFactory, creates and manages the flyweight objects.
 *
 */
public class FlyweightFactory {

  private static Map<String, Flyweight> flyweights = new HashMap<String, Flyweight>();

  /**
   * Returns Flyweight object. Just for sake of example following logic is
   * applied, if key starts with phrase:unshared than UnsharedConcreteFlyweight
   * object is created. Otherwise ConcreteFlyweight object is created.
   * 
   * @param key
   * @return Flyweight
   * 
   */
  public static Flyweight getFlyweight(String key, String value) {

    if (key.startsWith("unshared")) {
      flyweights.put(key, new UnsharedConcreteFlyweight(value));
    } else {
      if (!flyweights.containsKey(key)) {
        flyweights.put(key, new ConcreteFlyweight(value));
      }
    }

    return (Flyweight) flyweights.get(key);
  }
}

Usage

FlyweightTest.java

package com.hundredwordsgof.flyweight;

import static org.junit.Assert.*;
import org.junit.Test;

/**
 * Test Flyweight pattern.
 */
public class FlyweightTest {

  @Test
  public void testFlyweight() {

    Flyweight flyweight1 = FlyweightFactory.getFlyweight("key1", "value1");
    Flyweight flyweight2 = FlyweightFactory.getFlyweight("key2", "value2");
    Flyweight flyweight3 = FlyweightFactory.getFlyweight("key1", "value3");

    Flyweight unsharedFlyweight1 = FlyweightFactory.getFlyweight("unsharedKey1",
        "value1");
    Flyweight unsharedFlyweight2 = FlyweightFactory.getFlyweight("unsharedKey2",
        "value2");
    Flyweight unsharedFlyweight3 = FlyweightFactory.getFlyweight("unsharedKey1",
        "value3");

    assertNotEquals(flyweight1, flyweight2);
    assertEquals(flyweight1, flyweight3);
    assertNotEquals(flyweight2, flyweight3);

    assertNotEquals(unsharedFlyweight1, unsharedFlyweight2);
    assertNotEquals(unsharedFlyweight1, unsharedFlyweight3);
    assertNotEquals(unsharedFlyweight2, unsharedFlyweight3);

    if (flyweight1 instanceof com.hundredwordsgof.flyweight.ConcreteFlyweight) {
      assertEquals("value1",
          ((com.hundredwordsgof.flyweight.ConcreteFlyweight) flyweight1)
              .getIntrinsicState());
    }

    if (flyweight2 instanceof com.hundredwordsgof.flyweight.ConcreteFlyweight) {
      assertEquals("value2",
          ((com.hundredwordsgof.flyweight.ConcreteFlyweight) flyweight2)
              .getIntrinsicState());
    }

    if (flyweight3 instanceof com.hundredwordsgof.flyweight.ConcreteFlyweight) {
      assertEquals("value1",
          ((com.hundredwordsgof.flyweight.ConcreteFlyweight) flyweight3)
              .getIntrinsicState());
    }

    if (unsharedFlyweight1 instanceof com.hundredwordsgof.flyweight.UnsharedConcreteFlyweight) {
      assertEquals("value1",
          ((com.hundredwordsgof.flyweight.UnsharedConcreteFlyweight) unsharedFlyweight1)
              .getState());
    }

    if (unsharedFlyweight2 instanceof com.hundredwordsgof.flyweight.UnsharedConcreteFlyweight) {
      assertEquals("value2",
          ((com.hundredwordsgof.flyweight.UnsharedConcreteFlyweight) unsharedFlyweight2)
              .getState());
    }

    if (unsharedFlyweight3 instanceof com.hundredwordsgof.flyweight.UnsharedConcreteFlyweight) {
      assertEquals("value3",
          ((com.hundredwordsgof.flyweight.UnsharedConcreteFlyweight) unsharedFlyweight3)
              .getState());
    }
  }
}