Design Pattern Examples
Overview of object-oriented design patterns
Flyweight_Exercise.cpp
Go to the documentation of this file.
1
6
7#include <iostream>
8#include <sstream>
9
10#include "helpers/sleep.h"
11#include "helpers/cursor.h"
12#include "helpers/readkey.h"
13#include "helpers/checkforkey.h"
14#include "helpers/formatstring.h"
15
16#include "Flyweight_Exercise.h"
17#include "Flyweight_Classes.h"
18
19namespace // Anonymous
20{
21 using namespace DesignPatternExamples_cpp;
22
38 int _Flyweight_GenerateBigResource(int numImages, int width, int height)
39 {
40 if (numImages < 1)
41 {
42 numImages = 1;
43 }
44 else if (numImages > 9)
45 {
46 numImages = 9;
47 }
48 if (width < 3)
49 {
50 width = 3;
51 }
52 if (height < 3)
53 {
54 height = 3;
55 }
56
57 std::vector<std::string> image;
58 for (int row = 0; row < height; ++row)
59 {
60 std::string image_row("");
61 for (int imageIndex = 0; imageIndex < numImages; imageIndex++)
62 {
63 if (row == 0 || (row + 1) == height)
64 {
65 // top and bottom row are the same.
66 image_row += Helpers::formatstring("+%s+", std::string((size_t)width - 2, '-').c_str());
67 }
68 else
69 {
70 // All other rows are each the same -- except that
71 // each image is "numbered" where the background of the
72 // image reflects the number of the image (0, 1, 2, etc.).
73 char c = Helpers::formatstring("%d", imageIndex)[0];
74 image_row += Helpers::formatstring("|%s|", std::string((size_t)width - 2, c).c_str());
75 }
76 }
77 image.push_back(image_row);
78 }
79
80 int resourceId = BigResourceManager::AddResource(image);
81 return resourceId;
82 }
83
84
89 void _Flyweight_ClearDisplay(std::vector<std::vector<char>>& display)
90 {
91 for (std::vector<char>& row : display)
92 {
93 for (size_t index = 0; index < row.size(); ++index)
94 {
95 row[index] = '~';
96 }
97 }
98 }
99
100
107 std::vector<std::vector<char>> _Flyweight_GenerateDisplay(int width, int height)
108 {
109 std::vector<std::vector<char>> display;
110
111 for (int row = 0; row < height; ++row)
112 {
113 display.push_back(std::vector<char>(width));
114 }
115
117 return display;
118 }
119
120
125 void _Flyweight_ShowDisplay(std::vector<std::vector<char>>& display)
126 {
127 std::ostringstream output;
128 for (auto& row : display)
129 {
130 output << " ";
131 for (size_t col = 0; col < row.size(); col++)
132 {
133 output << row[col];
134 }
135 output << std::endl;
136 }
137 std::cout << output.str() << std::endl;
138 }
139
140
151 FlyweightClassList& flyweightInstances,
152 int display_width, int display_height)
153 {
154 for (FlyweightClassList::iterator flyweightIter = std::begin(flyweightInstances);
155 flyweightIter != std::end(flyweightInstances);
156 flyweightIter++)
157 {
158 Flyweight_Class* flyweight = (*flyweightIter).get();
159 Flyweight_Context context = flyweight->Context();
160 int image_width = flyweight->ImageWidth();
161 int image_height = flyweight->ImageHeight();
162 double newx = context.Position_X + context.Velocity_X;
163 double newy = context.Position_Y + context.Velocity_Y;
164
165 if (newx < 0 || (newx + image_width) > display_width)
166 {
167 // Bounce off left or right edge.
168 context.Velocity_X = -context.Velocity_X;
169 if (newx < 0)
170 {
171 newx = 0;
172 }
173 else
174 {
175 newx = display_width - image_width;
176 }
177 }
178
179 if (newy < 0 || (newy + image_height) > display_height)
180 {
181 // Bounce off top or bottom edge.
182 context.Velocity_Y = -context.Velocity_Y;
183 if (newy < 0)
184 {
185 newy = 0;
186 }
187 else
188 {
189 newy = display_height - image_height;
190 }
191 }
192
193 context.Position_X = newx;
194 context.Position_Y = newy;
195 flyweight->SetContext(context);
196 }
197 }
198
199
205 void _Flyweight_RenderFlyweights(FlyweightClassList& flyweightInstances, std::vector<std::vector<char>>& displayArea)
206 {
207 // Render the image into the "display", one image for each instance
208 // of the Flyweight class.
209 for (FlyweightClassList::iterator flyweightIter = std::begin(flyweightInstances);
210 flyweightIter != std::end(flyweightInstances);
211 flyweightIter++)
212 {
213 Flyweight_Class* flyweight = (*flyweightIter).get();
214 Flyweight_Context context = flyweight->Context();
215 flyweight->Render(displayArea,
216 context.OffsetXToImage,
217 flyweight->ImageWidth(),
218 flyweight->ImageHeight(),
219 (int)context.Position_X,
220 (int)context.Position_Y);
221 }
222 }
223
230 double GenerateVelocity()
231 {
232 double speed = ((rand() % 5) + 1) / 5.0;
233 double direction = ((rand() % 100) > 50) ? 1.0 : -1.0;
234 return speed * direction;
235 }
236
253 FlyweightClassList _Flyweight_GenerateFlyweightClasses(int bigResourceId, int numFlyweights,
254 int image_width, int image_height, int display_width, int display_height)
255 {
256 FlyweightClassList flyweightInstances;
257
258 // Generate the instances of the flyweight class, randomizing the position
259 // of each flyweight within the display.
260 for (int index = 0; index < numFlyweights; ++index)
261 {
262 Flyweight_Context context;
263 context.OffsetXToImage = index * image_width;
264 context.ImageWidth = image_width;
265 context.ImageHeight = image_height;
266 // Make sure the entire image can be rendered at each position
267 context.Position_X = rand() % (display_width - image_width);
268 context.Position_Y = rand() % (display_height - image_height);
269 // Randomize the initial velocity.
270 context.Velocity_X = GenerateVelocity();
271 context.Velocity_Y = GenerateVelocity();
272
273 // Create an instance of the Flyweight_Class for the given big
274 // resource and with the new context.
275 flyweightInstances.push_back(BigResourceManager::CreateFlyweight(bigResourceId, context));
276 }
277 return flyweightInstances;
278 }
279
280} // end namespace Anonmyous
281
282
284{
285
305 // ! [Using Flyweight in C++]
307 {
308 std::cout << std::endl;
309 std::cout << "Flyweight Exercise" << std::endl;
310
311 // Define the display and image size.
312 const int DISPLAY_WIDTH = 80;
313 const int DISPLAY_HEIGHT = 20;
314 const int IMAGE_WIDTH = 30;
315 const int IMAGE_HEIGHT = 5;
316 const int NUMFLYWEIGHTS = 5;
317 const int NUM_ITERATIONS = 1000;
318
319 int bigResourceId = _Flyweight_GenerateBigResource(NUMFLYWEIGHTS, IMAGE_WIDTH, IMAGE_HEIGHT);
320 FlyweightClassList flyweightInstances;
321 flyweightInstances = _Flyweight_GenerateFlyweightClasses(bigResourceId, NUMFLYWEIGHTS,
322 IMAGE_WIDTH, IMAGE_HEIGHT, DISPLAY_WIDTH, DISPLAY_HEIGHT);
323
324 // Create the "display".
325 // We use a list of character arrays so we can write to each
326 // character position individually. In C#, strings are immutable
327 // and changing a character in a string is not allowed.
328 std::vector<std::vector<char>> displayArea = _Flyweight_GenerateDisplay(DISPLAY_WIDTH, DISPLAY_HEIGHT);
329
330 // Finally, display the rendered output.
331 std::cout << Helpers::formatstring(" The image rendered %d times:", NUMFLYWEIGHTS) << std::endl;
332 std::cout << std::endl; // Blank line for iteration count
333 _Flyweight_RenderFlyweights(flyweightInstances, displayArea);
334 _Flyweight_ShowDisplay(displayArea);
335
337 // Now let's have some fun and bounce those images around for a while!
338 // (Or until a keypress.)
339 int cursorLeft = -1;
340 int cursorTop = -1;
341 Helpers::getcursorposition(&cursorTop, &cursorLeft);
342 if (cursorLeft != -1 && cursorTop != -1)
343 {
344 cursorTop -= DISPLAY_HEIGHT + 1;
345 }
346 for (int index = 0; index < NUM_ITERATIONS; ++index)
347 {
348 if (cursorLeft != -1)
349 {
350 Helpers::setcursorposition(cursorTop - 1, cursorLeft);
351 }
352 std::cout << Helpers::formatstring(" %5d/%d iterations [press a key to exit early]", index + 1, NUM_ITERATIONS) << std::endl;
353 if (cursorLeft != -1)
354 {
355 Helpers::setcursorposition(cursorTop, cursorLeft);
356 }
357
358 _Flyweight_ClearDisplay(displayArea);
359 _Flyweight_MoveFlyweights(flyweightInstances, DISPLAY_WIDTH, DISPLAY_HEIGHT);
360 _Flyweight_RenderFlyweights(flyweightInstances, displayArea);
361 _Flyweight_ShowDisplay(displayArea);
362 Helpers::sleep(16); // 60 frames a second
364 {
366 break;
367 }
368 }
370
371 std::cout << " Done." << std::endl;
372 }
373 // ! [Using Flyweight in C++]
374
375} // end namespace
Declaration of the Flyweight_Context struct, and the Flyweight_Class, BigResource,...
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.
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,...
static int AddResource(std::vector< std::string > rawResource)
Add a new big resource and return the ID of the resource.
static Flyweight_Class::unique_ptr_t CreateFlyweight(int bigResourceId, Flyweight_Context context)
Create a new instance of the Flyweight_Class associated with the given big resource and a context,...
Associates a context with a big resource.
void Render(std::vector< std::vector< char > > &display, int offset_x, int image_width, int image_height, int position_x, int position_y)
Render the image associated with this flyweight instance into the given display at the given position...
Flyweight_Context Context()
Retrieve the context for this class instance.
void SetContext(Flyweight_Context context)
Set the context for this class instance.
int ImageHeight()
Retrieve the "image" height from underlying big resource.
int ImageWidth()
Retrieve the "image" width from underlying big resource.
Declaration of the Flyweight_Exercise() function as used in the Flyweight Pattern.
The namespace containing all Design Pattern Examples implemented in C++.
std::vector< Flyweight_Class::unique_ptr_t > FlyweightClassList
void Flyweight_Exercise()
Example of using the Flyweight design pattern.
void enableinputecho()
Enable echoing input, which should be the default mode. Call this only after calling disableinputecho...
Definition: cursor.cpp:129
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.
Definition: cursor.cpp:118
int readkey()
Read a key from the keyboard, blocking if no key is pressed.
Definition: readkey.cpp:17
void sleep(int milliseconds)
Sleep for the specified number of milliseconds. Does not return until after the sleep period.
Definition: sleep.cpp:15
bool checkforkey()
Determine if a key has been pressed.
Definition: checkforkey.cpp:46
void setcursorposition(int row, int column)
Move the text cursor to the specified screen coordinates.
Definition: cursor.cpp:140
void getcursorposition(int *row, int *column)
Retrieve the current cursor position in the console window.
Definition: cursor.cpp:149
Represents the context for an instance of the Flyweight_Class. In this case, the context includes pos...