1, Introduction to Xiangyuan mode
1.1 definitions
- Definition of Flyweight pattern: using sharing technology to effectively support the reuse of a large number of fine-grained objects. By sharing existing objects, it can greatly reduce the number of objects to be created and avoid the overhead of a large number of similar classes, so as to improve the utilization of system resources.
- The main advantage of meta sharing mode is that only one copy of the same object is saved, which reduces the number of objects in the system, thus reducing the pressure on memory caused by fine-grained objects in the system
1.2 principle of sharing mode
There are two requirements in the shared meta model, fine-grained and shared objects. Because fine-grained is required, the number of objects and their properties will inevitably increase. At this time, we will divide the information of these objects into two parts: internal state and external state.
- Internal state refers to the information shared by objects, which is stored in the shared meta information and will not change with the change of environment.
- The external state refers to a tag on which an object can depend. It changes with the change of the environment and cannot be shared.
- The essence of meta sharing mode is to cache shared objects and reduce memory consumption.
1.2 structure of sharing mode
The main roles of Xiangyuan mode are as follows:
- Abstract shared element role (Flyweight): it is the base class of all specific shared element classes. It is the public interface to be implemented by the specific shared element specification. The external state of non shared elements is passed in the form of parameters through methods.
- Concrete Flyweight role: implement the interface specified in the abstract flyweight role.
- Unsharable flyweight role: it is an external state that cannot be shared. It is injected into the relevant methods of specific shared elements in the form of parameters.
- Flyweight Factory role: it is responsible for creating and managing a membership role. When a client object requests a membership object, the membership factory checks whether there is a qualified membership object in the system. If so, it will provide it to the client; if not, it will create a new membership object.
Structure diagram of sharing mode, where:
- UnsharedConcreteFlyweight is a non shared meta role, which contains non shared external status information info;
- Flyweight is an abstract shared element role, which contains the shared element method operation (unshared concrete flyweight state). The external state of non shared elements is passed in the form of parameters through this method;
- ConcreteFlyweight is a specific meta role, including the keyword key, which implements the abstract meta interface;
- FlyweightFactory is the role of sharing element factory. It is the keyword key to manage specific sharing elements;
The customer role obtains the specific share through the share factory and accesses the relevant methods of the specific share.
1.3 code implementation
Meta role abstract class definition:
/** * Description: Meta role Abstract Class * <br/> * Flyweight * * @author laiql * @date 2021/10/27 16:15 */ public abstract class Flyweight { /** * Internal state */ private String intrinsic; /** * External state */ protected final String extrinsic; protected Flyweight(String extrinsic) { this.extrinsic = extrinsic; } /** * Abstract definition of business operations * * @param extrinsic */ public abstract void operate(int extrinsic); public String getIntrinsic() { return intrinsic; } public void setIntrinsic(String intrinsic) { this.intrinsic = intrinsic; } }
External status specific meta role definitions:
/** * Description: Inherit Flyweight superclass or implement Flyweight interface, and increase storage space for its internal state * <br/> * ConcreteFlyweight * * @author laiql * @date 2021/10/27 16:19 */ public class ConcreteFlyweight extends Flyweight { /** * Accept external status * * @param extrinsic */ protected ConcreteFlyweight(String extrinsic) { super(extrinsic); } /** * Logical processing according to external state * * @param extrinsic */ @Override public void operate(int extrinsic) { System.out.println("specific Flyweight:" + extrinsic); } }
Non shared role definition:
/** * Description: Refers to those Flyweight subclasses that do not need to be shared * <br/> * UnsharedConcreteFlyweight * * @author laiql * @date 2021/10/27 16:20 */ public class UnsharedConcreteFlyweight extends Flyweight { protected UnsharedConcreteFlyweight(String extrinsic) { super(extrinsic); } @Override public void operate(int extrinsic) { System.out.println("Specific information not shared Flyweight:" + extrinsic); } }
Shared factory role definition:
/** * Description: A shared element factory is used to create and manage Flyweight objects, mainly to ensure reasonable sharing of Flyweight. When a user requests a Flyweight, the FlyweightFactory object provides a created instance or creates an instance * <br/> * FlyweightFactory * * @author laiql * @date 2021/10/27 16:21 */ public class FlyweightFactory { /** * Define a pool container */ private static HashMap<String, Flyweight> pool = new HashMap<>(); public static Flyweight getFlyweight(String extrinsic) { Flyweight flyweight = null; if (pool.containsKey(extrinsic)) { flyweight = pool.get(extrinsic); System.out.print("already existing " + extrinsic + " Directly from the pool---->"); } else { //Create meta objects based on external state flyweight = new ConcreteFlyweight(extrinsic); //Put into shared pool pool.put(extrinsic, flyweight); System.out.print("establish " + extrinsic + " And remove it from the pool---->"); } return flyweight; } }
Test case definition:
/** * Description: Meta mode test case * <br/> * Client * * @author laiql * @date 2021/10/27 16:24 */ public class Client { public static void main(String[] args) { int extrinsic = 22; Flyweight flyweightX = FlyweightFactory.getFlyweight("X"); flyweightX.operate(++extrinsic); Flyweight flyweightY = FlyweightFactory.getFlyweight("Y"); flyweightY.operate(++extrinsic); Flyweight flyweightZ = FlyweightFactory.getFlyweight("Z"); flyweightZ.operate(++extrinsic); Flyweight flyweightReX = FlyweightFactory.getFlyweight("X"); flyweightReX.operate(++extrinsic); Flyweight unsharedFlyweight = new UnsharedConcreteFlyweight("X"); unsharedFlyweight.operate(++extrinsic); } }
Expected result of use case:
Connected to the target VM, address: '127.0.0.1:54195', transport: 'socket' establish X And remove it from the pool---->specific Flyweight:23 establish Y And remove it from the pool---->specific Flyweight:24 establish Z And remove it from the pool---->specific Flyweight:25 already existing X Directly from the pool---->specific Flyweight:26 Specific information not shared Flyweight:27 Disconnected from the target VM, address: '127.0.0.1:54195', transport: 'socket'
2, Application scenario of meta mode
2.1 scenario introduction
- When there are a large number of objects in the system, and these objects consume a lot of memory, or most of the states of these objects can be externalized, the shared mode can be used to implement the buffer pool in these scenarios.
- In addition, the idea of this design mode is to reduce the use of memory and improve efficiency. It is similar to the prototype mode we used before to generate complex objects by cloning objects and reduce rpc calls Is this kind of thought.
2.2 application method
Judge with the unique identification code. If there is in memory, return the object identified by the unique identification code and store it in HashMap/HashTable.
2.3 code implementation
Simulate a data source cache pooling scenario
To create a data source class definition:
/** * Description: Data source object definition * <br/> * DataSource * * @author laiql * @date 2021/10/27 17:10 */ @Data @AllArgsConstructor @NoArgsConstructor public class DataSource { private String driveClassName; private String username; private String password; }
Abstract meta role data source class definition:
/** * Description: Meta abstract data source object * <br/> * AbstractDataSource * * @author laiql * @date 2021/10/27 17:13 */ public abstract class AbstractDataSource { protected DataSource dataSource; public AbstractDataSource(DataSource dataSource) { this.dataSource = dataSource; } //Class that delegates work public abstract DataSource getDatasource(); }
Specific data source object definitions:
/** * Description: Specific data source object * <br/> * DruidDataSource * * @author laiql * @date 2021/10/27 17:16 */ public class DruidDataSource extends AbstractDataSource { public DruidDataSource(DataSource dataSource) { super(dataSource); } @Override public DataSource getDatasource() { System.out.println(super.dataSource.toString()); return super.dataSource; } }
Shared factory data source class definition:
/** * Description: Data source factory class * <br/> * DataSourceFactory * * @author laiql * @date 2021/10/27 17:17 */ public class DataSourceFactory { private static Map<String, DruidDataSource> POOL = new ConcurrentHashMap<>(265); public static AbstractDataSource getDatasource(String key) { if (!POOL.containsKey(key)) { DataSource dataSource = new DataSource(); dataSource.setUsername("root"); dataSource.setPassword("root"); dataSource.setDriveClassName("com.mysql.jdbc.Drive"); POOL.put(key, new DruidDataSource(dataSource)); System.out.print("establish " + key + " And remove it from the pool---->"); return POOL.get(key); } System.out.print("already existing " + key + " Directly from the pool---->"); return POOL.get(key); } }
Test case definition:
@Test public void flyweightTest() { AbstractDataSource datasource = DataSourceFactory.getDatasource("K"); datasource.getDatasource(); DataSourceFactory.getDatasource("K").getDatasource(); DataSourceFactory.getDatasource("Y").getDatasource(); }
Expected output:
Connected to the target VM, address: '127.0.0.1:59586', transport: 'socket' establish K And remove it from the pool---->DataSource(driveClassName=com.mysql.jdbc.Drive, username=root, password=root) already existing K Directly from the pool---->DataSource(driveClassName=com.mysql.jdbc.Drive, username=root, password=root) establish Y And remove it from the pool---->DataSource(driveClassName=com.mysql.jdbc.Drive, username=root, password=root) Disconnected from the target VM, address: '127.0.0.1:59586', transport: 'socket'
2.4 advantages and disadvantages of Yuan sharing mode
- advantage:
It greatly reduces the creation of objects, reduces the occupation of program memory and improves efficiency
- Disadvantages:
The complexity of the system is improved. It is necessary to separate the internal state and the external state, and the external state has the solidification characteristics and should not change with the change of the internal state
3, Summary
- For the design of the shared element mode, we can focus on the design of the shared element factory. In some scenarios where a large number of repeated objects can be reused, using this scenario to reduce the call of the interface on the server and reduce the memory occupation on the client is the main application mode of this design mode.
- In addition, it can be seen from the use of the map structure that using a fixed id to store and obtain objects is a very key point. It is not only used in the shared element mode, but also in some other factory modes, adapter modes and composite modes, the map structure storage service can be used for external acquisition, so as to reduce the judgment of ifelse.
- Of course, in addition to the advantages of this design in reducing the use of memory, it also has its disadvantages. In some complex business processing scenarios, it is difficult to distinguish between internal and external states. If the state cannot be well split, the design of the shared factory will be very chaotic and difficult to maintain.