🔥 core
Meta sharing mode reduces the memory occupied by objects by sharing the same state shared by multiple objects.
The essence of meta mode is caching.
🙁 Problem scenario
You are a game map modeler, building complex game maps.
Now, you need to build a forest. There are thousands of trees in the forest. No tree has attributes such as position (x, y), height (h), color and texture. You made thousands of trees and built a beautiful and spectacular forest.
You can't wait to send the map project to your friend and let him experience the forest. Although the map project works perfectly on your computer, his computer crashes the moment he enters the map. You quickly check the log and find that the cause of the crash is "insufficient memory capacity". Of course, an ordinary game player's computer can't compare with a professional game map modeler.
You don't want to reduce every bit of the forest, and you have to make the map project run in limited memory. What shall I do?
🙂 Solution
The meta model may be more complex than you think, so please pay attention. 3.2. 1. Start.
The meta model puts forward two concepts: "internal state" and "external state", which are the classification of member variables.
Internal status: relatively fixed information, which cannot identify the object and is stored in the meta object.
External state: information that can be changed. They are unique attributes of each object and are stored in each original object.
I know this concept is very abstract and difficult to understand. You might as well use your hands to classify the member variables of the tree in the game map:
Position (x, y): unique to each tree object, which is the external state;
Height (h): unique to each tree object, which is the external state;
Color: many trees have the same color and are relatively fixed, which is the internal state;
Texture: many trees have the same material, which is relatively fixed and is the internal state.
Therefore, the position and height (x, y, h) are still stored in each object as member variables, and the color and texture are encapsulated in the meta class.
Think about it. If you don't want to reduce the number of tree objects, what can you do to optimize? The size of each object. Every tree object now has so many member variables. Can the memory be small? Now, by encapsulating some of the member variables into the shared meta class, we can directly replace these member variables in the original object with the reference of the shared meta object, thus reducing the memory occupied by each object; From the overall point of view, a limited number of shared objects are reused by thousands of objects, so that this part of the data of shared objects does not need to be stored repeatedly. This is the essence of memory optimization in meta mode.
Understand and digest. Well, let's continue to introduce another concept: "enjoy yuan factory".
A meta factory is a cache pool that manages meta objects. If the required shared element can be found in the cache pool, it will directly return to reuse; If it is not found, create a new element and add it to the cache pool.
So far, the sharing mode has been introduced, or the composite sharing mode has been introduced. Next, we will introduce the simple meta mode. Before that, I suggest you read the above content.
...
...
...
...
Well, now suppose you have basically understood the above content.
The idea of simple meta sharing mode is not to change the memory size occupied by each object, but to reduce the number of objects - objects with the same internal state are actually the reuse of the same object, but the external state of the object is constantly changed.
MOBA games classify hero status, such as assassin, mage, auxiliary, warrior, etc. Now, there is an assassin hero object in the hero factory. Take it out and modify its attributes accordingly to get a new assassin hero. Although the new assassin hero was born, the first assassin hero no longer exists.
Therefore, the application scenarios of simple meta sharing mode are relatively limited. For example, in the forest map above, you can't modify the attributes of one tree object to get another tree object in order to save memory. This reuse will make there are only a few bare trees in your forest.
🌈 Interesting examples
Because the above example of "building a forest" is a little complex, we simply write code to help us fully understand the meta pattern.
tree class Tree { private int x; private int y; private int h; private Type type; public Tree(int x, int y, int h, Type type) { this.x = x; this.y = y; this.h = h; this.type = type; } public void plant() { System.out.println("Plant a tree~"); System.out.println("coordinate x: " + x); System.out.println("coordinate y: " + y); System.out.println("height: " + h); System.out.println("colour: " + type.getColor()); System.out.println("texture of material: " + type.getTextrue()); } } Enjoy meta class class Type { // They are green, yellow, brown and black static final String[] COLORS = {"green", "yellow", "brown", "black"}; // Followed by fir, oak, birch and locust static final String[] TEXTURES = {"fir", "oak", "birch", "locust"}; private String color; private String textrue; public Type(String color, String textrue) { this.color = color; this.textrue = textrue; } public String getColor() { return color; } public String getTextrue() { return textrue; } } Xiangyuan factory class TypeFactory { private Map<String, Type> typeCache = new HashMap<>(); public Type getType(String color, String textrue) { String key = color + textrue; if (!typeCache.containsKey(key)) { typeCache.put(key, new Type(color, textrue)); } return typeCache.get(key); } } forest class Forest { public List<Tree> trees = new ArrayList<>(); public void init() { TypeFactory typeFactory = new TypeFactory(); // Build 1000 trees // Each tree has random position, random height, random color and random material for (int i = 0; i < 1000; i++) { Type type = typeFactory.getType(initColor(), initTexture()); Tree tree = new Tree(initX(), initY(), initH(), type); tree.plant(); trees.add(tree); } } private int initX() { return (int) (Math.random() * 10000); } private int initY() { return (int) (Math.random() * 10000); } private int initH() { return (int) (Math.random() * 10); } private String initColor() { return Type.COLORS[(int) (Math.random() * Type.COLORS.length)]; } private String initTexture() { return Type.TEXTURES[(int) (Math.random() * Type.TEXTURES.length)]; } }
public class FlyweightPatternDemo { public static void main(String[] args) { // Initialize forest // That is, planting trees new Forest().init(); } }
Plant a tree~ coordinate x: 9198 coordinate y: 8446 height: 6 colour: green texture of material: oak Plant a tree~ coordinate x: 6862 coordinate y: 9088 height: 1 colour: green texture of material: birch Plant a tree~ coordinate x: 7010 coordinate y: 6307 height: 0 colour: black texture of material: locust Plant a tree~ coordinate x: 3470 coordinate y: 9363 height: 5 colour: brown texture of material: birch Plant a tree~ coordinate x: 1287 coordinate y: 9743 height: 2 colour: black texture of material: fir Plant a tree~ coordinate x: 5547 coordinate y: 403 height: 8 colour: black texture of material: oak ...1000 more
☘️ Usage scenario
◾ ﹥ only use meta mode when the program must support a large number of objects and does not have enough memory capacity.
The benefit of applying this model depends on the way and scenario of using it. It is most effective when the program needs to generate a large number of similar objects; This will exhaust all memory of the target device; Objects contain duplicate states that can be extracted and shared among multiple objects.
🧊 Implementation mode
(1) Split the class member variable that needs to be rewritten into two parts:
- Intrinsic state: a member variable that contains immutable data that can be reused in many objects.
- External state: member variables containing different scenario data of each object
(2) Keep the member variables representing the intrinsic state in the class and set their properties to be immutable. These variables can only get the initial value in the constructor.
(3) Find all methods that use external state member variables, create a new parameter for each member variable used in the method, and use the parameter instead of the member variable.
(4) You can optionally create a factory class to manage the shared element cache pool. It is responsible for checking the existing shared elements when creating a new shared element. If you choose to use a factory, clients can only request shared elements through the factory. They need to pass the internal state of shared elements to the factory as a parameter.
(5) The client must store and calculate the value of the external state (scenario), because only in this way can the method of the shared element object be called. For ease of use, the external state and the member variables referencing the shared element can be moved to a separate scenario class.
🎲 Advantages and disadvantages
➕ If there are many similar objects in the program, you can save a lot of memory.
➕ Scenarios that require buffer pools.
➖ You may need to sacrifice execution speed in exchange for memory, because others need to recalculate some scenario data every time they call the meta method.
➖ The code becomes more complex. New members of the team always ask, "why split the state of an entity like this?".
🌸 supplement
compound element sharing mode / simple element sharing mode is a difficulty in design mode.
so please be patient to understand and experience > <.