Introduction to Shared and Shared mode (Flyweight mode) of Java design patterns

  • 2020-04-01 03:41:32
  • OfStack

Flyweight definition: avoid the overhead (such as memory consumption) of a large number of small classes with the same content, so that everyone shares a class (metaclass).

Why share/share mode

The principles of object-oriented languages is that everything is an object, but if use truly, object number may sometimes seem to be very big, such as word processing software, if every word as an object, thousands of words, the number of objects is thousands of, there is no doubt that the memory cost, so we still need to "seeking common ground while putting aside differences", find out the object group have in common, design a metaclass, packaging can be Shared classes, in addition, there are some characteristics is depends on the application (context), is not to be Shared, Two important concepts in the Flyweight internal state the intrinsic and extrinsic external state.

To put it in a nutshell, you start with the original model, and then, depending on the situation and environment, you produce a specific model with different characteristics. Obviously, you need to generate different new objects here, so Factory mode is often used in Flyweight mode. The Flyweight's internal state is Shared, and the Flyweight factory maintains a Flyweight pool(pattern pool) to hold objects with internal state.

Flyweight mode is an efficient and performance-enhancing mode that makes programs run faster. There are many applications: if you want to read a series of strings from a database, many of which are duplicates, you can store them in a Flyweight pool.

How to use the Shared/Shared mode

Let's start with the Flyweight abstract interface:


public interface Flyweight{
 public void operation( ExtrinsicState state );
}
//The abstract data type (self-designed) used for this schema
public interface ExtrinsicState { }

Here is the concrete implementation of the interface (ConcreteFlyweight) and adding memory space for the internal state. The ConcreteFlyweight must be shareable and any state it holds must be internal (intrinsic). That is, the ConcreteFlyweight must be independent of its application context.

public class ConcreteFlyweight implements Flyweight {
 private IntrinsicState state;
 public void operation( ExtrinsicState state ){
   //Operation
 }
}

Of course, not all Flyweight concrete classes need to be Shared, so there's another ConcreteFlyweight that isn't Shared:

public class UnsharedConcreteFlyweight implements Flyweight {
 public void operation( ExtrinsicState state ) { }
}

The Flyweight factory is responsible for maintaining a Flyweight pool (storing internal state). When a client requests a Shared Flyweight, the factory first searches whether there is already an applicable one in the pool. If so, the factory simply returns and sends out the object.

public class FlyweightFactory {
 //Flyweight pool
 private Hashtable flyweights = new Hashtable();
 public Flyweight getFlyweight( Object key ) {
  Flyweight flyweight = (Flyweight) flyweights.get(key);
  if( flyweight == null ) {
   //Generate a new ConcreteFlyweight
   flyweight = new ConcreteFlyweight();
   flyweights.put( key, flyweight );
  }
   return flyweight;
 }
}

Now that the basic framework for Flyweight mode is in place, let's look at how to call:


FlyweightFactory factory = new FlyweightFactory();
Flyweight fly1 = factory.getFlyweight( "Fred" );
Flyweight fly2 = factory.getFlyweight( "Wilma" );
......

From the point of view of the call, it seems to be a pure use of Factory, but the secret lies in the inner design of the Factory.

The Flyweight schema is used in data sources such as XML
As we mentioned above, when a large number of strings are read from the data source, there must be duplicates, then we can use the Flyweight pattern to improve efficiency. For example, in the case of record CDS, there are multiple CDS in one XML file.

Each CD has three fields:

1. Release date (year)
2. Singer's name and other information (artist)
3. Album track (title)

In this case, the singer's name may be repeated, that is, there may be multiple CDS of different songs from different periods by the same singer. We will be "singer name" as shareable ConcreteFlyweight. The other two fields as UnsharedConcreteFlyweight.

First look at the contents of the data source XML file:


<?xml version="1.0"?>
<collection> <cd>
<title>Another Green World</title>
<year>1978</year>
<artist>Eno, Brian</artist>
</cd> <cd>
<title>Greatest Hits</title>
<year>1950</year>
<artist>Holiday, Billie</artist>
</cd> <cd>
<title>Taking Tiger Mountain (by strategy)</title>
<year>1977</year>
<artist>Eno, Brian</artist>
</cd>
....... </collection>

Although there are only three CDS in the example above, CDS can be considered a large number of repeating small classes, because the components have only three fields and there are repeated (singer name).

The CD is similar to the interface above, Flyweight:


public class CD {
 private String title;
 private int year;
 private Artist artist;  public String getTitle() {return title;}
 public int getYear() {return year;}
 public Artist getArtist() {return artist;}  public void setTitle(String t){title = t;}
 public void setYear(int y){year = y;}
 public void setArtist(Artist a){artist = a;}
}

The name of the singer as a shareable ConcreteFlyweight:


public class Artist {
 //Internal state
 private String name;  // note that Artist is immutable.
 String getName(){return name;}  Artist(String n){
     name = n;
    }
}

Look at the Flyweight factory, specifically for making the above shareable ConcreteFlyweight:Artist


public class ArtistFactory {
 Hashtable pool = new Hashtable();
 Artist getArtist(String key){
  Artist result;
  result = (Artist)pool.get(key);
  ////Create a new Artist
  if(result == null) {
   result = new Artist(key);
   pool.put(key,result);  
  }
  return result;
    }
}

When you have a few thousand or more CDS, Flyweight mode saves more space, and the more flyweights you share, the more space you save.


Related articles: