0 x0, introduction
🤡 has tested the BUG of such modification and tackled the beauty of Design Patterns. This paper corresponds to design patterns and paradigms: structural type (54-55), Flyweight Pattern, and structural design Pattern, the last one ~
- Easy to understand → share element, the unit to be shared
- Intent → Save memory, reuse immutable objects
- In other words → find common features between similar objects, and then reuse those features.
An example of image: the development of the game, a forest scene, tens of thousands of trees, each tree instantiate different model, memory explosion directly, you can extract all the trees there are properties of an object, move to a single class, and then only need a class instance, and then every tree in the forest for the instance for a reference (photo from: The Flyweight) :
Tips: Secondary knowledge processing inevitably has mistakes, interested in the time can refer to the original, thank you.
Write a simple example of playing cards
If you were to implement a simple poker program, the code might look something like this (assuming no Kings, only 52 cards, 4 suits) :
// Card class, have suit and size two attributes
public class Card {
private String color;
private String num;
public Card(String color, String num) {
this.color = color;
this.num = num;
}
public String printMsg(a) { return "Playing cards" + color + num +"】"; }}// Test case
public class Player {
public static void main(String[] args) {
String[] colors = new String[]{"Spade"."Red heart"."Plum blossom"."Square"};
List<Card> cards = new ArrayList<>();
for (String color : colors) {
for (int i = 1; i <= 13; i++) {
switch (i) {
case 11: cards.add(new Card(color, "J")); break;
case 12: cards.add(new Card(color, "Q")); break;
case 13: cards.add(new Card(color, "K")); break;
default: cards.add(new Card(color, i + "")); break;
}
}
}
System.out.println("The deck is started. Total." + cards.size() + "Zhang");
System.out.println("Five cards at random:");
for (int i = 0; i < 5; i++) System.out.println(cards.get((int) (Math.random() * 52)).printMsg()); }}Copy the code
The output is as follows:
Normal output, but 52 Card objects initialized,? How many do I need to create with the share pattern?
Draw the common attributes of playing cards: suit and size, suit fixed four, size change, write a card’s parent class, write four suit subclass inheritance:
// Enjoy metaclass (abstract class or interface)
abstract class AbstractCard {
// A common operation to implement on shared objects, using an external state as an input parameter (saved by the client, changed at run time)
abstract String printMsg(String num);
}
// Specify the metaclass
class SpadeCard extends AbstractCard {
@Override String printMsg(String num) { return "Spade"+ num; }}class HeartCard extends AbstractCard {
@Override String printMsg(String num) { return "Red heart"+ num; }}class SpadeCard extends AbstractCard {
@Override String printMsg(String num) { return "Spade"+ num; }}class DiamondCard extends AbstractCard {
@Override String printMsg(String num) { return "Square"+ num; }}// Xiangyuan factory
public class PokerFactory {
public static final int SPADE = 0; / / spades
public static final int HEART = 1; / / the hearts
public static final int CLUB = 2; / / the plum blossom
public static final int DIAMOND = 3; / / square
public static Map<Integer, AbstractCard> pokers = new HashMap<>();
public static AbstractCard getPoker(int color) {
// Do not call containsKey again
AbstractCard card = pokers.get(color);
if(card == null) {
System.out.println("Color object does not exist, new object...");
switch (color) {
case SPADE: card = new SpadeCard(); break;
case HEART: card = new HeartCard(); break;
case CLUB: card = new ClubCard(); break;
default: card = new DiamondCard(); break;
}
pokers.put(color, card);
} else {
System.out.println("Color object already exists, reuse object...");
}
returncard; }}// Test case
public class Player {
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
AbstractCard card;
// Random color
switch ((int) (Math.random() * 4)) {
case 0: card = PokerFactory.getPoker(PokerFactory.SPADE); break;
case 1: card = PokerFactory.getPoker(PokerFactory.HEART); break;
case 2: card = PokerFactory.getPoker(PokerFactory.CLUB); break;
default: card = PokerFactory.getPoker(PokerFactory.DIAMOND); break;
}
// Random size
int num = (int)(Math.random() * 13 + 1);
switch (num) {
case 11: System.out.println(card.printMsg("J")); break;
case 12: System.out.println(card.printMsg("Q")); break;
case 13: System.out.println(card.printMsg("K")); break;
default: System.out.println(card.printMsg(num + "")); break; }}}}Copy the code
The output is as follows:
Only four card objects are created using the share mode, bringing up the concept of internal state and external state:
- Internal state: fixed, shareable parts stored inside the share object, such as suits here;
- External state: Variable parts that are not shareable, usually passed inside the share object by the client, such as the size here;
Of course, the distinction between the state is not absolute, depending on the scene, such as the expansion to the doulandlord game, the internal state has become 54 cards (how to deal will not exceed 54 cards), the external state has become the card holder. Extends to chess games, internal states (color, text), external states (position information, etc.).
Along the way, UML class diagrams and constituent roles:
- Flyweight (enjoy meta-class) → Abstract class or interface, define the common operation method to implement the enjoy meta-object, the method will pass in external state parameters;
- ConcreteFlyweight → Implement meta interface and add storage space for internal state;
- FlyweightFactory → create and manage shared weightweight objects and provide external access interfaces;
The essence of the Share mode:
Minimize the memory consumption of creating duplicate objects by creating more common characteristics of reusable objects.
Disadvantages:
Time for space is not suitable for the system that needs rapid response. Internal and external states need to be separated, which is difficult to unify and increases the complexity of system design and implementation.
0x2, Share mode VS Multi-instance, Cache, object pool
From the point of view of code implementation, the meta pattern is very similar to the multiple instances in the singleton we learned earlier, but from the point of view of design intent, it is completely different.
Multi-example is to limit the number of objects, and share mode is to reuse objects and save memory.
Let’s look at the differences with caching:
The share pattern emphasizes spatial efficiency (reuse of big data model objects), while the cache pattern emphasizes time efficiency (such as caching of live data and inventory data, etc., which may occupy a large amount of space for timely response).
There are also object pools:
To avoid memory fragmentation caused by frequent object creation and release, a contiguous memory space is allocated in advance. Each time an object is created, a free object is directly removed from the object pool for use, and then returned to the object pool for future use, rather than directly released.
Reuse in pooling technology can be understood as reuse, the main purpose is to save time, at any time, each object is exclusive to a user. The reuse of the sharing mode can be understood as shared use, the main purpose is to save space, in the whole life cycle, is shared by all users.
0x3. Application of The Meta schema to Java Integer and String
Integer first, the following code:
Integer i1 = 12;
Integer i2 = 12;
Integer i3 = 128;
Integer i4 = 128;
System.out.println(i1 == i2); // Output: true
System.out.println(i3 == i4); // Output: false
Copy the code
** Double equals (==)** Treats two data types differently:
- Basic data types (byte, char, int, etc.) → whether the comparison values are the same;
- Reference data type (object instance) → compare whether the object memory address that the variable points to is the same;
If i1 and i2 are equal, then i3 and i4 should also be equal. Listen to me:
Integer i1 = 12,12 is the basic data type int, and is assigned to the wrapper type Integer. The automatic boxing mechanism is triggered.
// Create an instance of type Integer and assign a value
Integer i1 = Integer.valueOf(12);
Int j = i1 int j = i1
int j = i1.intValue();
Copy the code
Ok, so here’s what the valueOf() method does:
If the value is greater than low and less than high, return an Integer instance in the cache, otherwise return a new Integer instance.
When the IntegerCache class is loaded, it initializes the Integer instances between -128 and 127 and adds them to the cache array. The reason for caching only this range is:
It is not possible to cache all integer values in advance, only the most common or most common integer value for most applications, which is the size of a byte.
The maximum value can also be modified by modifying the JVM configuration (optionally) :
-Djava.lang.Integer.IntegerCache.high=255
-XX:AutoBoxCacheMax=255
Copy the code
This is why the above code outputs true and false (reuse in the range -128-127, direct new outside the range), where IntegerCache is the share factory in the share mode.
Now look at String, with code like this:
String s1 = "Jacob";
String s2 = "Jacob";
String s3 = "Jay" + "Elder brother";
String s4 = new String("Jacob");
System.out.println(s1 == s2); // Output: true
System.out.println(s1 == s3); // Output: true
System.out.println(s1 == s4); // Output: false
Copy the code
Similar to the design of the Integer class, the JVM has a dedicated storage area for string constants (the string constant pool). Unlike Integer, there is no way to know in advance which string constants are to be shared. String constants are stored in the pool only when they are used for the first time. Explain the above code:
- S1 == s2 → at compile time, “jack” is stored in the constant pool, s1 and s2 reference to the “jack” in the constant pool;
- S1 == s3 → compiler optimization, first string splice, then go to the constant pool to find whether the string exists, so that s3 directly refers to the string;
- s1 ! = s4 → Instead of explicitly assigning, the compiler reallocates an area in the heap to store its object data;
That’s all for this section, we’ll start with 11 behavioral design patterns, thank you