Diagram of the flyweight pattern
The flyweight pattern is used to represent some resource that can be shared multiple times. The flyweight class contains just enough information to identify the resource and some context in which the instance of each flyweight class is used. Instances of flyweight classes are always obtained through a class factory that is provided with the context information as a parameter.
The intent is to represent the shared resource with a small, lightweight class instance that can be used many times without incurring large memory penalties that might occur if the shared resource was actually duplicated many times.
An early form of flyweight class is a handle to a resource. The handle was typically a 32-bit value and could be easily shared and passed around with minimal impact to the system. The drawback is context information could not be easily attached to the handle.
The contents of a flyweight class typically consists of a handle to the resource and some context information. The context information is provided by the caller to the flyweight factory to be inserted into a new instance of the flyweight class.
How to Use
This example uses an image as the "big" resource. The image is composed of a set of smaller images, all the same size, and all arranged in a single horizontal row. Each smaller image is associated with one Flyweight class instance and the smaller image's background is the number of the Flyweight class instance. For example, for 3 flyweight instances:
+----------------------------++----------------------------++----------------------------+
|0000000000000000000000000000||1111111111111111111111111111||2222222222222222222222222222|
|0000000000000000000000000000||1111111111111111111111111111||2222222222222222222222222222|
|0000000000000000000000000000||1111111111111111111111111111||2222222222222222222222222222|
+----------------------------++----------------------------++----------------------------+
The flyweight class represents a position for that image as the context, along with an offset into the big resource to the left edge of that flyweight class instance's image. The flyweight class also contains velocity information for moving the flyweight instance around a display. Of course, everything is in text but it's the thought that counts.
Example output:
Flyweight Exercise
The image rendered 5 times:
39/1000 iterations [press a key to exit early]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+----------------------------+~~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~|0000000000000000000000000000|~~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~|0000000000000000000000000000|~~
~~~~~~+----------------------------+~~~~~~~~~~~~|0000000000000000000000000000|~~
~~~~~~|1111111111111111111111111111|~~~~~~~~~~~~+----------------------------+~~
~~~~~~|1111111111111111111111111111|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~|1111111111111111111111111111|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~+----------------------------+---+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~|222222222222222222222222+----------------------------+~~~~~~~~~~~~~~~~~~~~~~~
~~|222222222222222222222222|4444444444444444444444444444|~~~~~~~~~~~~~~~~~~~~~~~
~~|222222222222222222222222|4444444444444444444444444444|------------+~~~~~~~~~~
~~+------------------------|4444444444444444444444444444|333333333333|~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~~+----------------------------+333333333333|~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~|3333333333333333333333333333|~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+----------------------------+~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Done.
C++
void Flyweight_Exercise()
{
std::cout << std::endl;
std::cout << "Flyweight Exercise" << std::endl;
const int DISPLAY_WIDTH = 80;
const int DISPLAY_HEIGHT = 20;
const int IMAGE_WIDTH = 30;
const int IMAGE_HEIGHT = 5;
const int NUMFLYWEIGHTS = 5;
const int NUM_ITERATIONS = 1000;
FlyweightClassList flyweightInstances;
IMAGE_WIDTH, IMAGE_HEIGHT, DISPLAY_WIDTH, DISPLAY_HEIGHT);
std::cout << std::endl;
int cursorLeft = -1;
int cursorTop = -1;
if (cursorLeft != -1 && cursorTop != -1)
{
cursorTop -= DISPLAY_HEIGHT + 1;
}
for (int index = 0; index < NUM_ITERATIONS; ++index)
{
if (cursorLeft != -1)
{
}
std::cout <<
Helpers::formatstring(
" %5d/%d iterations [press a key to exit early]", index + 1, NUM_ITERATIONS) << std::endl;
if (cursorLeft != -1)
{
}
{
break;
}
}
std::cout << " Done." << std::endl;
}
static void _Flyweight_GenerateDisplay(Display *display, int width, int height)
Generate a display area in which to render the big resource.
static void _Flyweight_MoveFlyweights(Flyweight_ImageList *imageList, int display_width, int display_height)
Move the given flyweight instances within the display, bouncing them off the edges of the display.
static void _Flyweight_GenerateFlyweightClasses(int bigResourceId, int numFlyweights, int image_width, int image_height, int display_width, int display_height, Flyweight_ImageList *imageList)
Helper function to generate the specified number of Flyweight_image objects and associate those objec...
static void _Flyweight_RenderFlyweights(Flyweight_ImageList *imageList, Display *displayArea)
Render the image into the display, once for each flyweight instance.
static void _Flyweight_ClearDisplay(Display *display)
Clear the "display" to a background image, erasing whatever was there before.
static void _Flyweight_ShowDisplay(Display *display)
Render the display to the screen.
static int _Flyweight_GenerateBigResource(int numImages, int width, int height)
Generate a big resource, in this case, a text master "image" of the specified height,...
void enableinputecho()
Enable echoing input, which should be the default mode. Call this only after calling disableinputecho...
std::string formatstring(const char *fmt,...)
Use the given string and arguments to return a buffer containing the formatted string....
void disableinputecho()
Disable echoing input until enableinputecho() is called.
int readkey()
Read a key from the keyboard, blocking if no key is pressed.
void sleep(int milliseconds)
Sleep for the specified number of milliseconds. Does not return until after the sleep period.
bool checkforkey()
Determine if a key has been pressed.
void setcursorposition(int row, int column)
Move the text cursor to the specified screen coordinates.
void getcursorposition(int *row, int *column)
Retrieve the current cursor position in the console window.
C#
public void Run()
{
Console.WriteLine();
Console.WriteLine("Flyweight Exercise");
const int DISPLAY_WIDTH = 80;
const int DISPLAY_HEIGHT = 20;
const int IMAGE_WIDTH = 30;
const int IMAGE_HEIGHT = 5;
const int NUMFLYWEIGHTS = 5;
const int NUM_ITERATIONS = 1000;
List<Flyweight_Class> flyweightInstances;
IMAGE_WIDTH, IMAGE_HEIGHT, DISPLAY_WIDTH, DISPLAY_HEIGHT);
Console.WriteLine(" The image rendered {0} times:", NUMFLYWEIGHTS);
Console.WriteLine("");
int cursorLeft = Console.CursorLeft;
int cursorTop = Console.CursorTop;
cursorTop -= DISPLAY_HEIGHT + 1;
for (int index = 0; index < NUM_ITERATIONS; ++index)
{
Console.SetCursorPosition(cursorLeft, cursorTop - 1);
Console.WriteLine(" {0,5}/{1} iterations [press a key to exit early]", index + 1, NUM_ITERATIONS);
Console.SetCursorPosition(cursorLeft, cursorTop);
System.Threading.Thread.Sleep(16);
if (Console.KeyAvailable)
{
Console.ReadKey();
break;
}
}
Console.WriteLine(" Done.");
}
Python
def Flyweight_Exercise():
print()
print("Flyweight Exercise")
DISPLAY_WIDTH = 80
DISPLAY_HEIGHT = 20
IMAGE_WIDTH = 30
IMAGE_HEIGHT = 5
NUMFLYWEIGHTS = 5
NUM_ITERATIONS = 1000
IMAGE_WIDTH, IMAGE_HEIGHT, DISPLAY_WIDTH, DISPLAY_HEIGHT)
print(" The image rendered {0} times:".format(NUMFLYWEIGHTS))
print()
cursorLeft, cursorTop = helpers.getcursorposition()
if cursorLeft != -1 and cursorTop != -1:
cursorTop -= DISPLAY_HEIGHT + 1
for index in range(0, NUM_ITERATIONS):
if cursorLeft != -1:
helpers.setcursorposition(cursorLeft, cursorTop - 1)
print(" {0:5}/{1} iterations [press a key to exit early]".format(index + 1, NUM_ITERATIONS))
if cursorLeft != -1:
helpers.setcursorposition(cursorLeft, cursorTop)
helpers.sleep(16)
if helpers.checkforkey():
helpers.readkey()
break
print(" Done.")
The namespace containing all the "helper" functions in the C++ code.
C
void Flyweight_Exercise(void)
{
printf("\nFlyweight_Exercise\n");
const int DISPLAY_WIDTH = 80;
const int DISPLAY_HEIGHT = 20;
const int IMAGE_WIDTH = 30;
const int IMAGE_HEIGHT = 5;
const int NUMFLYWEIGHTS = 5;
const int NUM_ITERATIONS = 1000;
printf("bigResourceId = %d\n", bigResourceId);
IMAGE_WIDTH, IMAGE_HEIGHT, DISPLAY_WIDTH, DISPLAY_HEIGHT, &imageList);
printf(" The image rendered %d times:\n", NUMFLYWEIGHTS);
printf("\n");
int cursorLeft = -1;
int cursorTop = -1;
if (cursorLeft != -1 && cursorTop != -1)
{
cursorTop -= DISPLAY_HEIGHT + 1;
}
for (int index = 0; index < NUM_ITERATIONS; ++index)
{
if (cursorLeft != -1)
{
}
printf(" %5d/%d iterations [press a key to exit early]\n", index + 1, NUM_ITERATIONS);
if (cursorLeft != -1)
{
}
{
break;
}
}
printf(" Done.\n");
}
void BigResourceManager_Clear(void)
Release all resources owned by the Big Resource Manager.
void Display_Destroy(Display *display)
Destroy the "display" window in the given Display object by releasing the memory associated with it....
void Flyweight_ImageList_Clear(Flyweight_ImageList *imageList)
Clear the given Flyweight_ImageList object by freeing up all allocated Flyweight_image objects and re...
bool checkforkey(void)
Determine if a key has been pressed.
void disableinputecho(void)
Disable echoing input until enableinputecho() is called.
void enableinputecho(void)
Enable echoing input, which should be the default mode. Call this only after calling disableinputecho...
void setcursorposition(int row, int column)
Move the text cursor to the specified screen coordinates.
void getcursorposition(int *row, int *column)
Retrieve the current cursor position in the console window.
int readkey(void)
Read a key from the keyboard, blocking if no key is pressed. Use the checkforkey() function to see if...
void sleep(int milliseconds)
Sleep for the specified number of milliseconds. Does not return until after the sleep period.
Represents a "display" window, in which to render Flyweight images. This "display" window is then pri...
Represents an expandable list of Flyweight_Image objects. This is managed by the Flyweight_ImageList_...
Rust
(Apologies. Doxygen does not understand Rust syntax and therefore cannot colorize the code.)
pub fn flyweight_exercise() -> Result<(), String> {
println!("");
println!("Flyweight Exercise");
let mut big_resource_manager = BigResourceManager::new();
let big_resource = _flyweight_generate_big_resource(NUMFLYWEIGHTS, IMAGE_WIDTH, IMAGE_HEIGHT);
let resource_id = big_resource_manager.add_resource(big_resource);
println!("bigResourceId = {resource_id}");
let mut flyweight_images: Vec<FlyweightImage> = Vec::new();
_flyweight_generate_flyweight_images(resource_id, NUMFLYWEIGHTS,
IMAGE_WIDTH, IMAGE_HEIGHT, DISPLAY_WIDTH, DISPLAY_HEIGHT,
&mut flyweight_images);
// Create the "display".
// We use a list of character arrays so we can write to each
// character position individually. In Rust, strings are immutable
// and changing a character in a string is not allowed.
let mut display = _fylweight_generate_display(DISPLAY_WIDTH, DISPLAY_HEIGHT);
// Finally, display the rendered output.
println!(" The image rendered {} times:", NUMFLYWEIGHTS);
println!(); // Blank line for iteration count
_flyweight_render_images(&big_resource_manager, &flyweight_images, &mut display);
_flyweight_show_display(&display);
// Initialize raw key input before getting cursor position.
cursor::hide_cursor();
// Now let's have some fun and bounce those images around for a while!
// (Or until a keypress.)
let (cursor_left, mut cursor_top) = cursor::get_cursor_position();
cursor_top -= (DISPLAY_HEIGHT + 1) as u16;
for index in 0..NUM_ITERATIONS {
cursor::set_cursor_position(cursor_left, cursor_top - 1);
println!(" {:5}/{} iterations [press a key to exit early]", index + 1, NUM_ITERATIONS);
cursor::set_cursor_position(cursor_left, cursor_top);
_flyweight_clear_display(&mut display);
_flyweight_move_images(&mut flyweight_images, display.width, display.height);
_flyweight_render_images(&big_resource_manager, &flyweight_images, &mut display);
_flyweight_show_display(&display);
thread::sleep(Duration::from_millis(16)); // 60 frames a second
if key_input::check_for_key() {
break;
}
}
cursor::show_cursor();
println!(" Done.");
Ok(())
}
See Also