Design Pattern Examples
Overview of object-oriented design patterns
Flyweight_Exercise.c
Go to the documentation of this file.
1
5
6#include <stdbool.h>
7#include <stdio.h>
8#include <stdlib.h>
9#include <string.h>
10
11#include "helpers/checkforkey.h"
12#include "helpers/cursor.h"
13#include "helpers/readkey.h"
14#include "helpers/sleep.h"
15
17#include "Flyweight_Display.h"
18#include "Flyweight_Image.h"
19#include "Flyweight_Exercise.h"
20
21//=============================================================================
22//=============================================================================
23
40static int _Flyweight_GenerateBigResource(int numImages, int width, int height)
41{
42 int resourceId = -1;
43
44 if (numImages < 1)
45 {
46 numImages = 1;
47 }
48 else if (numImages > 9)
49 {
50 numImages = 9;
51 }
52 if (width < 3)
53 {
54 width = 3;
55 }
56 if (height < 3)
57 {
58 height = 3;
59 }
60
61 size_t imageSize = (width * height) * numImages;
62 BigResource* resource = calloc(1, sizeof(BigResource));
63 if (resource != NULL)
64 {
65 char* image = calloc(1, imageSize + 1);
66 if (image != NULL)
67 {
68 const char imageBackgroundCharacter[] = "0123456789";
69
70 resource->data = image;
71 resource->numImages = numImages;
72
73 for (int row = 0; row < height; ++row)
74 {
75 int imageColumnOffset = row * (width * numImages);
76 for (int imageIndex = 0; imageIndex < numImages; imageIndex++)
77 {
78 char backgroundChar = imageBackgroundCharacter[imageIndex];
79 for (int colIndex = 0; colIndex < width; colIndex++)
80 {
81 if (row == 0 || (row + 1) == height)
82 {
83 // top and bottom row are the same.
84 if (colIndex == 0 || (colIndex + 1) == width)
85 {
86 image[imageColumnOffset + colIndex] = '+';
87 }
88 else
89 {
90 image[imageColumnOffset + colIndex] = '-';
91 }
92 }
93 else
94 {
95 // top and bottom row are the same.
96 if (colIndex == 0 || (colIndex + 1) == width)
97 {
98 image[imageColumnOffset + colIndex] = '|';
99 }
100 else
101 {
102 // All other rows are each the same -- except that
103 // each image is "numbered" where the background of the
104 // image reflects the number of the image (0, 1, 2, etc.).
105 image[imageColumnOffset + colIndex] = backgroundChar;
106 }
107 }
108 }
109 imageColumnOffset += width;
110 }
111 }
112
113 resourceId = BigResourceManager_AddResource(resource);
114 if (resourceId == -1)
115 {
116 free(image);
117 image = NULL;
118 }
119 }
120 else
121 {
122 printf(" Error! Failed to allocate memory for the big image: size = %zu\n", imageSize);
123 }
124 }
125 else
126 {
127 printf(" Error! Failed to allocate memory for the big resource structure: size = %zu\n", sizeof(BigResource));
128 }
129 return resourceId;
130}
131
132
143 int display_width, int display_height)
144{
145 if (imageList != NULL)
146 {
147 for (size_t imageIndex = 0; imageIndex < imageList->images_count; imageIndex++)
148 {
149 Flyweight_Image* image = &imageList->images[imageIndex];
150 Flyweight_Context* context = &image->Context;
151 int image_width = context->ImageWidth;
152 int image_height = context->ImageHeight;
153 double newx = context->Position_X + context->Velocity_X;
154 double newy = context->Position_Y + context->Velocity_Y;
155
156 if (newx < 0 || (newx + image_width) > display_width)
157 {
158 // Bounce off left or right edge.
159 context->Velocity_X = -context->Velocity_X;
160 if (newx < 0)
161 {
162 newx = 0;
163 }
164 else
165 {
166 newx = display_width - image_width;
167 }
168 }
169
170 if (newy < 0 || (newy + image_height) > display_height)
171 {
172 // Bounce off top or bottom edge.
173 context->Velocity_Y = -context->Velocity_Y;
174 if (newy < 0)
175 {
176 newy = 0;
177 }
178 else
179 {
180 newy = display_height - image_height;
181 }
182 }
183
184 context->Position_X = newx;
185 context->Position_Y = newy;
186 }
187 }
188}
189
190
196static void _Flyweight_RenderFlyweights(Flyweight_ImageList* imageList, Display* displayArea)
197{
198 if (imageList != NULL && displayArea != NULL)
199 {
200 // Render the image into the "display", one image for each instance
201 // of the Flyweight class.
202 for (size_t imageIndex = 0; imageIndex < imageList->images_count; imageIndex++)
203 {
204 Flyweight_Image* image = &imageList->images[imageIndex];
205 Flyweight_Context* context = &image->Context;
206 BigResource_Render(displayArea, image->BigResourceId,
207 context->OffsetXToImage,
208 context->ImageWidth,
209 context->ImageHeight,
210 (int)context->Position_X,
211 (int)context->Position_Y);
212 }
213 }
214}
215
216
223static double GenerateVelocity(void)
224{
225 double speed = ((rand() % 5) + 1) / 5.0;
226 double direction = ((rand() % 100) > 50) ? 1.0 : -1.0;
227 return speed * direction;
228}
229
230
246static void _Flyweight_GenerateFlyweightClasses(int bigResourceId, int numFlyweights,
247 int image_width, int image_height, int display_width, int display_height,
248 Flyweight_ImageList* imageList)
249{
250 // Generate the instances of the flyweight class, randomizing the position
251 // of each flyweight within the display.
252 for (int index = 0; index < numFlyweights; ++index)
253 {
254 Flyweight_Image image = { 0 };
255 image.Context.OffsetXToImage = index * image_width;
256 image.Context.ImageWidth = image_width;
257 image.Context.ImageHeight = image_height;
258 // Make sure the entire image can be rendered at each position
259 image.Context.Position_X = rand() % (display_width - image_width);
260 image.Context.Position_Y = rand() % (display_height - image_height);
261 // Randomize the initial velocity.
264
265 // Create an instance of the Flyweight_Class for the given big
266 // resource and with the new context.
267 image.BigResourceId = bigResourceId;
268 Flyweight_ImageList_Add(imageList, &image);
269 }
270}
271
272
277static void _Flyweight_ClearDisplay(Display* display)
278{
279 if (display != NULL && display->area != NULL)
280 {
281 for (int rowIndex = 0; rowIndex < display->height; rowIndex++)
282 {
283 char* row = display->area[rowIndex];
284 for (int colIndex = 0; colIndex < display->width; ++colIndex)
285 {
286 row[colIndex] = '~';
287 }
288 }
289 }
290}
291
292
299static void _Flyweight_GenerateDisplay(Display* display, int width, int height)
300{
301 if (Display_Create(display, width, height))
302 {
304 }
305}
306
311static void _Flyweight_ShowDisplay(Display* display)
312{
313 if (display != NULL && display->area != NULL)
314 {
315 for (int rowIndex = 0; rowIndex < display->height; rowIndex++)
316 {
317 printf(" %s\n", display->area[rowIndex]);
318 }
319 printf("\n");
320 }
321}
322
323//=============================================================================
324//=============================================================================
325
345// ! [Using Flyweight in C]
347{
348 printf("\nFlyweight_Exercise\n");
349
350 // Define the display and image size.
351 const int DISPLAY_WIDTH = 80;
352 const int DISPLAY_HEIGHT = 20;
353 const int IMAGE_WIDTH = 30;
354 const int IMAGE_HEIGHT = 5;
355 const int NUMFLYWEIGHTS = 5;
356 const int NUM_ITERATIONS = 1000;
357
358 int bigResourceId = _Flyweight_GenerateBigResource(NUMFLYWEIGHTS, IMAGE_WIDTH, IMAGE_HEIGHT);
359 printf("bigResourceId = %d\n", bigResourceId);
360
361 Flyweight_ImageList imageList = { 0 };
362 _Flyweight_GenerateFlyweightClasses(bigResourceId, NUMFLYWEIGHTS,
363 IMAGE_WIDTH, IMAGE_HEIGHT, DISPLAY_WIDTH, DISPLAY_HEIGHT, &imageList);
364
365 // Create the "display".
366 // We use a list of character arrays so we can write to each
367 // character position individually. In C#, strings are immutable
368 // and changing a character in a string is not allowed.
369 Display displayArea = { 0 };
370 _Flyweight_GenerateDisplay(&displayArea, DISPLAY_WIDTH, DISPLAY_HEIGHT);
371
372 // Finally, display the rendered output.
373 printf(" The image rendered %d times:\n", NUMFLYWEIGHTS);
374 printf("\n"); // Blank line for iteration count
375 _Flyweight_RenderFlyweights(&imageList, &displayArea);
376 _Flyweight_ShowDisplay(&displayArea);
377
379 // Now let's have some fun and bounce those images around for a while!
380 // (Or until a keypress.)
381 int cursorLeft = -1;
382 int cursorTop = -1;
383 getcursorposition(&cursorTop, &cursorLeft);
384 if (cursorLeft != -1 && cursorTop != -1)
385 {
386 cursorTop -= DISPLAY_HEIGHT + 1;
387 }
388
389 for (int index = 0; index < NUM_ITERATIONS; ++index)
390 {
391 if (cursorLeft != -1)
392 {
393 setcursorposition(cursorTop - 1, cursorLeft);
394 }
395 printf(" %5d/%d iterations [press a key to exit early]\n", index + 1, NUM_ITERATIONS);
396 if (cursorLeft != -1)
397 {
398 setcursorposition(cursorTop, cursorLeft);
399 }
400
401 _Flyweight_ClearDisplay(&displayArea);
402 _Flyweight_MoveFlyweights(&imageList, DISPLAY_WIDTH, DISPLAY_HEIGHT);
403 _Flyweight_RenderFlyweights(&imageList, &displayArea);
404 _Flyweight_ShowDisplay(&displayArea);
405 sleep(16); // 60 frames a second
406 if (checkforkey())
407 {
408 readkey();
409 break;
410 }
411 }
413
414 // Cleanup
415 Display_Destroy(&displayArea);
416 Flyweight_ImageList_Clear(&imageList);
418
419 printf(" Done.\n");
420}
421// ! [Using Flyweight in C]
void BigResource_Render(Display *display, int bigResourceId, int offset_x, int image_width, int image_height, int position_x, int position_y)
Render the specified portion of the big resource into the given display at the given coordinates in t...
int BigResourceManager_AddResource(BigResource *rawResource)
Add a new big resource and return the ID of the resource. If the resource is successfully added,...
void BigResourceManager_Clear(void)
Release all resources owned by the Big Resource Manager.
Declaration of the Big Resource Manager functions, BigResourceManager_Clear(), BigResourceManager_Add...
bool Display_Create(Display *display, int width, int height)
Create a "display" window in the given Display object, with the given width and height.
void Display_Destroy(Display *display)
Destroy the "display" window in the given Display object by releasing the memory associated with it....
Declaration of the Display_Create() and Display_Destroy() functions for managing the Display structur...
static double GenerateVelocity(void)
Generate a random velocity, which includes a speed and a direction. The velocity is 0....
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.
void Flyweight_Exercise(void)
Example of using the Flyweight design pattern.
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 Flyweight_ImageList_Clear(Flyweight_ImageList *imageList)
Clear the given Flyweight_ImageList object by freeing up all allocated Flyweight_image objects and re...
void Flyweight_ImageList_Add(Flyweight_ImageList *imageList, Flyweight_Image *image)
Add a Flyweight_Image object to the given Flyweight_ImageList object. The list takes ownership of the...
Declaration of the Flyweight_ImageList and Flyweight_Image structures, the latter which wraps the big...
bool checkforkey(void)
Determine if a key has been pressed.
Definition: checkforkey.c:39
Declaration of the Flyweight_Exercise() function as used in the Flyweight Pattern.
void disableinputecho(void)
Disable echoing input until enableinputecho() is called.
Definition: cursor.c:112
void enableinputecho(void)
Enable echoing input, which should be the default mode. Call this only after calling disableinputecho...
Definition: cursor.c:123
void setcursorposition(int row, int column)
Move the text cursor to the specified screen coordinates.
Definition: cursor.c:134
void getcursorposition(int *row, int *column)
Retrieve the current cursor position in the console window.
Definition: cursor.c:143
int readkey(void)
Read a key from the keyboard, blocking if no key is pressed. Use the checkforkey() function to see if...
Definition: readkey.c:14
void sleep(int milliseconds)
Sleep for the specified number of milliseconds. Does not return until after the sleep period.
Definition: sleep.c:21
Represents a big image. Call the BigResource_Clear() function to release the memory used by each inst...
char * data
Image data, row-oriented.
int numImages
Number of images represented in the big image.
Represents a "display" window, in which to render Flyweight images. This "display" window is then pri...
int width
Width of each row.
int height
Height of each row.
char ** area
2-dimensional array of strings, representing rows. Each row is zero-terminated.
Represents the context for an instance of the Flyweight_Image structure. In this case,...
double Position_Y
Vertical position of upper left corner of image in a display.
int OffsetXToImage
Offset into big resource to left edge of image.
double Position_X
Horizontal position of upper left corner of image in a display.
int ImageHeight
Height of image.
int ImageWidth
Width of image.
double Velocity_X
Velocity to apply to the horizontal position.
double Velocity_Y
Velocity to apply to the vertical position.
Represents an image that associates a context with a big resource.
Flyweight_Context Context
The context associated with this image. The calling entity uses this context to manipulate the positi...
int BigResourceId
The big resource being referenced by this flyweight image. This is represented by a handle to the big...
Represents an expandable list of Flyweight_Image objects. This is managed by the Flyweight_ImageList_...
size_t images_count
Number of Flyweight_image objects in the list.
Flyweight_Image * images
Dynamic list of Flyweight_Image objects.