Design pattern series: http://aphysia.cn/categories/designpattern
The beginning is still like that. Please look down
What is the meta model?
FlyWeight mode is a kind of structural mode, which is mainly used to reduce the number of created objects, reduce memory consumption and improve performance. Speaking of this, I wonder if you think of pool technology, such as String constant pool, database connection pool, buffer pool, etc. yes, these all apply the meta mode.
For example, some objects require a lot of resources when they are created, the creation cost is relatively high, and the memory overhead is relatively large. If we keep creating and the machine can't afford it, then we think of pooling technology and put the created objects in the pool. When necessary, we can go to the pool to get them, that is, we share the objects in the pool, which is sharing.
Listen to the name, it's easy to share the bike:
Characteristics of sharing mode
Generally speaking, meta objects need to be used in different scenarios. If the state can be modified at will, it is easy to cause confusion and the probability of error increases greatly. However, if all internal attributes are immutable and not very flexible, in order to find a balance between stability and flexibility, general meta objects will divide internal attributes into two categories:
- Internal state: immutable, shared in multiple places, and reused parts can only be set through the constructor
- External state: each object may have different states in different scenes, which can be modified
- Simple meta sharing mode: in the simple meta sharing mode, all specific meta classes can be shared, and there are no non shared specific meta classes.
- Composite element sharing mode: some simple element sharing objects can be combined in combination mode to form composite element sharing objects. Such composite element sharing objects cannot be shared, but they can be decomposed into simple element sharing objects, and the latter can be shared
Here we are talking about the simple yuan sharing mode, which generally has several objects:
- Shared meta interface or abstract class (Flyweight): public methods are declared and defined in the interface or abstract class, which can provide some external capabilities or provide data on demand.
- Concrete flyweight: it implements an abstract shared class. Some internal data is immutable. When implementing the interface, it will provide some external capabilities or data.
- Flyweightfactory: a flyweightfactory is mainly used to create and manage shared objects. Various types of shared objects are placed in a pool, usually in the form of key value pairs. Of course, it can also be other types. If an object is obtained for the first time, it needs to be created first. If the object already exists in the pool, it can be returned directly.
realization
For example, when we go out to play, we need to buy air tickets. Suppose that the uniqueness of a flight is related to the flight number, departure time and arrival time. Users like to query the flight related information through the flight number. First, we need to create an interface for the flight:
public interface IFlight { void info(); }
Specific Flight class:
public class Flight implements IFlight { private String flightNo; private String start; private String end; private boolean isDelay; public Flight(String flightNo, String start, String end) { this.flightNo = flightNo; this.start = start; this.end = end; isDelay = Math.random() > 0.5; } @Override public void info() { System.out.println(String.format("from[%s]reach[%s]Your flight[%s]: %s ", start, end, flightNo, isDelay ? "Delayed takeoff" : "Normal takeoff")); } }
Flight search factory class FlightSearchFactory:
public class FlightSearchFactory { public static IFlight searchFlight(String flightNo,String start,String end){ return new Flight(flightNo,start,end); } }
Impersonate client requests:
public class ClientTest { public static void main(String[] args) { IFlight flight = FlightSearchFactory.searchFlight("C9876","Beijing","Shanghai"); flight.info(); } }
We can see the following information printed out:
from[Beijing]reach[Shanghai]Your flight[C9876]: Delayed takeoff
However, there is a problem above. Every time you visit, you will create an object. People on the same flight theoretically query the same data. In fact, this part can be shared and reused to improve efficiency. Why not?
How to cache?
We generally use HashMap to cache. We only need to define the uniquely identified key:
import java.util.HashMap; import java.util.Map; public class FlightSearchFactory { private static Map<String, IFlight> maps = new HashMap<>(); public static IFlight searchFlight(String flightNo, String start, String end) { String key = getKey(flightNo, start, end); IFlight flight = maps.get(key); if (flight == null) { System.out.print("No in cache, need to rebuild:"); flight = new Flight(flightNo, start, end); maps.put(key, flight); }else{ System.out.print("Read data from cache:"); } return flight; } private static String getKey(String flightNo, String start, String end) { return String.format("%s_%s_%s", flightNo, start, end); } }
Test code:
public class ClientTest { public static void main(String[] args) { IFlight flight = FlightSearchFactory.searchFlight("C9876","Beijing","Shanghai"); flight.info(); IFlight flight1 = FlightSearchFactory.searchFlight("C9876","Beijing","Shanghai"); flight1.info(); IFlight flight2 = FlightSearchFactory.searchFlight("H1213","Beijing","Guangzhou"); flight2.info(); } }
Test results:
No in cache, need to rebuild: from[Beijing]reach[Shanghai]Your flight[C9876]: Normal takeoff Read data from cache:[Beijing]reach[Shanghai]Your flight[C9876]: Normal takeoff No in cache, need to rebuild: from[Beijing]reach[Guangzhou]Your flight[H1213]: Normal takeoff
It can be seen that if there are in the cache, the objects will not be rebuilt and the purpose of sharing objects can be achieved. We usually use various connection pools in the project, such as Redis connection pool, Mysql connection pool, etc. these resources are essentially valuable and we can share them.
In fact, Integer in JDK also uses cache technology. Because we often use small values, the default Integer will read the cache content first if it is obtained by valuesOf(int i):
public static Integer valueOf(int i) { if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i); }
We can see that if the data in the range of low and high will be obtained from the cache, otherwise an object will be directly created. What is the range of low and high?
static final int low = -128; static final int high;
High is dynamic, but high is asserted. It must be greater than or equal to 127: assert integercache high >= 127;, The range can be from Java lang.Integer. IntegerCache. Read out the high configuration item:
static { // high value may be configured by property int h = 127; String integerCacheHighPropValue = sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high"); if (integerCacheHighPropValue != null) { try { int i = parseInt(integerCacheHighPropValue); i = Math.max(i, 127); // Maximum array size is Integer.MAX_VALUE h = Math.min(i, Integer.MAX_VALUE - (-low) -1); } catch( NumberFormatException nfe) { // If the property cannot be parsed into an int, ignore it. } } high = h; cache = new Integer[(high - low) + 1]; int j = low; for(int k = 0; k < cache.length; k++) cache[k] = new Integer(j++); // range [-128, 127] must be interned (JLS7 5.1.7) assert IntegerCache.high >= 127; }
Test:
public class IntegerTest { public static void main(String[] args) { // Unequal Integer integer = Integer.valueOf(128); Integer integer1 = Integer.valueOf(128); System.out.println(integer == integer1); // equal Integer integer2 = Integer.valueOf(127); Integer integer3 = Integer.valueOf(127); System.out.println(integer2 == integer3); // equal Integer integer4 = Integer.valueOf(0); Integer integer5 = Integer.valueOf(0); System.out.println(integer4 == integer5); // equal Integer integer6 = Integer.valueOf(-128); Integer integer7 = Integer.valueOf(-128); System.out.println(integer6 == integer7); // Unequal Integer integer8 = Integer.valueOf(-129); Integer integer9 = Integer.valueOf(-129); System.out.println(integer8 == integer9); } }
From the above results, we can see that Integer is actually cached from - 128 to 127, which also verifies our results. Note that Integer must be used Valueof() is a new object created by using the constructor new Integer().
summary
- Advantages: if there are many similar or repeated objects, using the shared element mode can save space
- Disadvantages: if there is a lot of reuse, special processing has been done in different places, and the code complexity increases
In fact, design pattern is a common design idea summarized in the continuous exploration of software engineering. It is not necessary to use it, not a silver bullet, but there is always something worth learning, understand the benefits of its design, and constantly improve. We write code, even if it is improved a little at a time. Once heard a saying: when you see someone else's code that is not elegant, you have the impulse to refactor it. You can read more of your own code and write better (roughly this means). Encourage each other!
[about the author]:
Qin Huai, the official account of Qin Huai grocery store, author's personal website: http://aphysia.cn , the road of technology is not for a while, with high mountains and long rivers. Even if it is slow, it will not stop.