Labels

Thursday, November 18, 2010

Flyweight Design Pattern - Structural

1.1          Intent


-       Use sharing to support large numbers of fine-grained objects efficiently.
-       Make instances of classes on the fly to improve performance efficiently, like individual characters or icons on the screen.

1.2          Motivation

-       A Flyweight is an object that minimizes memory occupation by sharing as much data as possible with other similar objects.
-       It is a way to use objects in large numbers when a simple representation would use an unacceptable amount of memory.
-       Often some parts of the object state can be shared and it's common to put them in external data structures and pass them to the flyweight objects temporarily when they are used.

The Flyweight pattern is used to avoid the overhead of large numbers of very similar classes. Sometimes you can greatly reduce the number of different classes that you need to instantiate if you can recognize that the instances are fundamentally the same except for a few parameters. If you can move those variables outside the class instance and pass them in as part of a method call, the number of separate instances can be greatly reduced by sharing them.

The Flyweight design pattern provides an approach for handling such classes. It refers to the instance's Intrinsic Data that makes the instance unique and the Extrinsic Data that is passed in as arguments. The Flyweight is appropriate for small, fine-grained classes like individual characters or icons on the screen.

Example –  A set of folders representing information about various people. Since these are so similar, they are candidates for the Flyweight pattern.



Another classic example usage of the flyweight pattern are the data structures for graphical representation of characters in a Word Processor. It would be nice to have, for each character in a document, a glyph object containing its font outline, font metrics, and other formatting data, but it would amount to hundreds or thousands of bytes for each character. Instead, for every character there might be a reference to a flyweight glyph object shared by every instance of the same character in the document; only the position of each character (in the document and/or the page) would need to be stored externally.

The following diagram shows how a document editor can use objects to represent characters.

               

The drawback of such a design is its cost. Even moderate-sized documents may require hundreds of thousands of character objects, which will consume lots of memory and may incur unacceptable run-time overhead. The Flyweight pattern describes how to share objects to allow their use at fine granularities without prohibitive cost.

Aflyweight 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. 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.

Flyweights model concepts or entities that are normally too plentiful to represent with objects. For example, a document editor can create a flyweight for each letter of the alphabet. Each flyweight stores a character code, but its coordinate position in the document and its typographic style can be determined from the text layout algorithms and formatting commands in effect wherever the character appears. The character code is intrinsic state, while the other information is extrinsic.
     
Logically there is an object for every occurrence of a given character in the document:



Physically, however, there is one shared flyweight object per character, and it appears in different contexts in the document structure. Each occurrence of a particular character object refers to the same instance in the shared pool of flyweight objects:

                                 

1.1          Applicability

·         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.

1.2          Structure




1.3          Participants




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.

UnsharedConcreteFlyweight   
  • not all Flyweight subclasses need to be shared. The Flyweight interface enables sharing, but it doesn't enforce it. It is common for UnsharedConcreteFlyweight objects to have ConcreteFlyweight objects as children at some level in the flyweight object structure (as the Row and Column classes have).

FlyweightFactory   
  • creates and manages flyweight objects
  • ensures that flyweight are shared properly. When a client requests a flyweight, the FlyweightFactory objects 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).


1.4          Sample Code

This code demonstrates the Flyweight pattern in which a relatively small number of objects is shared many times by different clients.



class FlyweightFactory
  {
    private Hashtable flyweights = new Hashtable();

    // Constructor
    public FlyweightFactory()
    {
      flyweights.Add("X", new ConcreteFlyweight());   
      flyweights.Add("Y", new ConcreteFlyweight());
      flyweights.Add("Z", new ConcreteFlyweight());
    }

    public Flyweight GetFlyweight(string key)
    {
      return((Flyweight)flyweights[key]);
    }
  }
abstract class Flyweight
  {
    public abstract void Operation(int extrinsicstate);
  }

class UnsharedConcreteFlyweight : Flyweight
  {
    public override void Operation(int extrinsicstate)
    {
      Console.WriteLine("UnsharedConcreteFlyweight: " +
        extrinsicstate);
    }
  }
class ConcreteFlyweight : Flyweight
  {
    public override void Operation(int extrinsicstate)
    {
      Console.WriteLine("ConcreteFlyweight: " + extrinsicstate);
    }
  }

class MainApp
  {
    static void Main()
    {
      // Arbitrary extrinsic state
      int extrinsicstate = 22;
   
      FlyweightFactory f = new FlyweightFactory();

      // Work with different flyweight instances
      Flyweight fx = f.GetFlyweight("X");
      fx.Operation(--extrinsicstate);

      Flyweight fy = f.GetFlyweight("Y");
      fy.Operation(--extrinsicstate);

      Flyweight fz = f.GetFlyweight("Z");
      fz.Operation(--extrinsicstate);

      UnsharedConcreteFlyweight uf = new
        UnsharedConcreteFlyweight();

      uf.Operation(--extrinsicstate);

      // Wait for user
      Console.Read();
    }
  }

Output –

ConcreteFlyweight: 21
ConcreteFlyweight: 20
ConcreteFlyweight: 19
UnsharedConcreteFlyweight: 18


Hope this helps.

Thanks & Regards,
Arun Manglick

No comments:

Post a Comment