Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Flyweight [Design Pattern]
#1
Flyweight

Hi, today i'm gonna show you guys how to use flyweight pattern in practice. But first what is flyweight.

Theory 


In computer programming, flyweight is a software design pattern. A flyweight is an object that minimizes memory usage by sharing as much data as possible with other similar objects; it is a way to use objects in large numbers when a simple repeated representation would use an unacceptable amount of memory. Often some parts of the object state can be shared, and it is common practice to hold them in external data structures and pass them to the objects temporarily when they are used.
A classic example usage of the flyweight pattern is the data structures for graphical representation of characters in a word processor. It might be desirable to have, for each character in a document, a glyph object containing its font outline, font metrics, and other formatting data, but this 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 internally.
Another example is string interning.
In other contexts the idea of sharing identical data structures is called hash consing.

from : wikipedia.org

Practice


In practice specially when we talk about using a this pattern in G2O. We for sure wanna use it on systems like creating mobs, vobs etc. Everything what need to have a lot of objects. Becouse the pattern is to save memory. We must ensure that there are no repeating constant variables in the object. Well just i will show that on practice. 

In this part i will try to create 10 wolfs. 

Code:
class MobIn {
    constructor(x,y,z,angle,world,hp,str,range,_name)
    {
        x = x;
        y = y;
        z = z;
        angle = angle;
        world = world;
        hp = hp;
        str = str;
        hpleft = hp;
        range = range;
               name = _name;
    }
    
    x = null;
    y = null;
    z = null;
    angle = null;
    world = null;
    hp = null;
    str = null;
    hpleft = null;
    range = null;
       name = null;
};

local wolfs = [
MobIn(11,4120,10,0,"NEWNEWWORLD//NEWWORLD.ZEN",40,10,1000,"Wolf"),
MobIn(12,10,14210,0,"NEWNEWWORLD//NEWWORLD.ZEN",40,10,1000,"Wolf"),
MobIn(1123,1124,14124,0,"NEWNEWWORLD//NEWWORLD.ZEN",40,10,1000,"Wolf"),
MobIn(14,10,12130,0,"NEWNEWWORLD//NEWWORLD.ZEN",40,10,1000,"Wolf"),
MobIn(1125,141,10,5,"NEWNEWWORLD//NEWWORLD.ZEN",40,10,1000,"Wolf"),
MobIn(16,10,11230,10,"NEWNEWWORLD//NEWWORLD.ZEN",40,10,1000,"Wolf"),
MobIn(1712,4120,112540,0,"NEWNEWWORLD//NEWWORLD.ZEN",40,10,1000,"Wolf"),
MobIn(1812,10,1140,0,"NEWNEWWORLD//NEWWORLD.ZEN",40,10,1000,"Wolf"),
MobIn(10,1210,1041,0,"NEWNEWWORLD//NEWWORLD.ZEN",40,10,1000,"Wolf"),
MobIn(1210,10,11240,0,"NEWNEWWORLD//NEWWORLD.ZEN",40,10,1000,"Wolf"),
]

Well as you see there is like ten wolf with same statistics same range. Only position is different. So we need to have more space to insert this statistics. So its kinda wasting of resources.

One of better way to do it is just to add class (Singleton) with will have all this statistics. Look.

Code:
/// We dont have singletone class so we use table.

// creating a hash table, which will contain shared statistics
local WolfStat = {
   maxHp = 40,
   str = 10,
   dex = 10,
   range = 100,
   name = "Wolf",
}

class MobIn {
   // shared stats between all objects
   static stats = WolfStat
   
   // initial fields which will be later copied for each instance
   x = null
   y = null
   z = null
   angle = null
   world = null
   
   hp = null

   constructor(_x,_y,_z,_angle,_world, _hp)
   {    
       // creating copy of these fields
       x = _x;
       y = _y;
       z = _z;
       angle = _angle;
       world = _world;
       
       hp = 40
   }
};

local wolfs = [
MobIn(11,4120,10,0,"NEWNEWWORLD//NEWWORLD.ZEN", 40),
MobIn(12,10,14210,0,"NEWNEWWORLD//NEWWORLD.ZEN", 40),
MobIn(1123,1124,14124,0,"NEWNEWWORLD//NEWWORLD.ZEN", 40),
MobIn(14,10,12130,0,"NEWNEWWORLD//NEWWORLD.ZEN", 40),
MobIn(1125,141,10,5,"NEWNEWWORLD//NEWWORLD.ZEN", 40),
MobIn(16,10,11230,10,"NEWNEWWORLD//NEWWORLD.ZEN", 40),
MobIn(1712,4120,112540,0,"NEWNEWWORLD//NEWWORLD.ZEN", 40),
MobIn(1812,10,1140,0,"NEWNEWWORLD//NEWWORLD.ZEN", 40),
MobIn(10,1210,1041,0,"NEWNEWWORLD//NEWWORLD.ZEN", 40),
MobIn(1210,10,11240,0,"NEWNEWWORLD//NEWWORLD.ZEN", 40),
]


Well its like okey thats nice but we could use other way to dont write it same times. Yes but that not the point. You have a lot of ways to do the same thing. Just to reach a goal with is save a memory.
Reply
#2
Your usage example isn't correct (all fields are copied anyway).
Good post, correct the example, and i hope, that you will add some more similar posts Big Grin.
Reply
#3
Correct version update - thx to Patrix
Reply
#4
It's worth to mention that this design pattern can be implemented to the existing code.
The current example (made by me) isn't showing this way, so i'm going to demonstrate one neat trick with use of metatables that allows us to easilly edit the existing class to use the flyweight pattern. Suppose that we have a class called Npc, let's just assume that it looks like more or less like this:

Squirrel Script
  1. class Npc
  2. {
  3. _maxHealth = 40
  4. _maxMana = 10
  5. // more fields and some methods & constructor.
  6. }



The problem is, that if we change this class by adding the static keyword before the attributes like this:

Squirrel Script
  1. class Npc
  2. {
  3. static _maxHealth = 40
  4. static _maxMana = 10
  5. // more fields and some methods & constructor.
  6. }



The error will occure when we try to get the static field via the object like this:

Squirrel Script
  1. local npc = Npc(...) // ... means some arguments
  2. print(npc._maxMana)



What we can do, is to add the _get metamethod which will be invoked each time, when we try to access the object field which doesn't exists.

Squirrel Script
  1. class Npc
  2. {
  3. static _maxHealth = 40
  4. static _maxMana = 10
  5.  
  6.  
  7. function _get(idx)
  8. {
  9. try
  10. return getclass()[idx]
  11. catch (msg)
  12. throw null
  13. }
  14. }



Now, when we try to access the field via the object, everything will be working as it should.
There is only one problem, having a static fields as types: int, string, null, bool, float will cause a problem with directly trying to modify them.
First of all, we can't modify the static field via the object, even if we provide the _set metamethod, it will still raise an error.
There is a way to add the desired functionallity by using the class.newmember or class.rawnewmember.

Squirrel Script
  1. class Npc
  2. {
  3. static _maxHealth = 40
  4. static _maxMana = 10
  5.  
  6.  
  7. function _get(idx)
  8. {
  9. try
  10. return getclass()[idx]
  11. catch (msg)
  12. throw null
  13. }
  14.  
  15. function _set(idx, val)
  16. {
  17. try
  18. getclass().newmember(idx, val, "", true)
  19. catch (msg)
  20. throw null
  21. }
  22. }



IMO however it's not required to supply the _set metamethod. Anyways, that's all i wanted to show, ofc _get metamethod can be implemented in different way allowing us to access the data from for example a table.
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  Decorator [Design Pattern] Patrix 1 2,711 10.09.2019, 07:56
Last Post: Quarchodron

Forum Jump:


Users browsing this thread: 1 Guest(s)