Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”.

This article also participated in the “Digitalstar Project” to win a creative gift package and creative incentive money

An overview of the

Definition:

Sharing technology is used to efficiently support 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 objects, thus improving the utilization of system resources.

structure

There are two states in Flyweight mode:

  1. Internal state, that is, shareable parts that do not change as the environment changes.
  2. External state refers to the part that changes with the environment and cannot be shared. The key to realize the sharing mode is to distinguish the two states in the application and externalize the external state.

The main roles of enjoy mode are as follows:

  • Abstract Flyweight: Usually an interface or abstract class that declares methods common to the concrete Flyweight metaclass. These methods can provide external data (internal state) of the Flyweight object and also set external data (external state) through these methods.
  • Concrete Flyweight role: It implements an abstract privilege metaclass called a privilege object; Storage space for internal state is provided in the concrete meta-class. Typically, we can design concrete meta-classes in conjunction with the singleton pattern, providing a unique meta-object for each concrete meta-class.
  • Unsharable Flyweight: Not all subclasses of the abstract Flyweight metaclass need to be shared. Subclasses that cannot be shared can be designed as unshared concrete flyweights. An object that does not share a concrete meta-class can be created by instantiation.
  • Flyweight Factory role: Responsible for creating and managing Flyweight roles. When a customer object requests a share object, the share factory checks whether there is a share object that meets the requirements in the system and provides it to the customer if there is one. If one does not exist, a new share object is created.

Case implementation

Tetris

The following picture is a well-known tetris in a square, if in the game Tetris, each different square is an instance object, these objects will take up a lot of memory space, the following use of share yuan mode to achieve.

Let’s start with the class diagram:

The code is as follows:

Tetris has different shapes, and abstractBoxes can be drawn up from these shapes to define common properties and behaviors.

public abstract class AbstractBox {
    public abstract String getShape(a);

    public void display(String color) {
        System.out.println("Square shape:" + this.getShape() + "Color:"+ color); }}Copy the code

The next step is to define different shapes: IBox class, LBox class, OBox class, etc.

public class IBox extends AbstractBox {

    @Override
    public String getShape(a) {
        return "I"; }}public class LBox extends AbstractBox {

    @Override
    public String getShape(a) {
        return "L"; }}public class OBox extends AbstractBox {

    @Override
    public String getShape(a) {
        return "O"; }}Copy the code

A factory class (BoxFactory) is provided to manage the meta-objects (aka AbstractBox subclass objects). Only one factory class object is required, so you can use the singleton pattern. And give the factory class a method to get the shape.

public class BoxFactory {

    private static HashMap<String, AbstractBox> map;

    private BoxFactory(a) {
        map = new HashMap<String, AbstractBox>();
        AbstractBox iBox = new IBox();
        AbstractBox lBox = new LBox();
        AbstractBox oBox = new OBox();
        map.put("I", iBox);
        map.put("L", lBox);
        map.put("O", oBox);
    }

    public static final BoxFactory getInstance(a) {
        return SingletonHolder.INSTANCE;
    }

    private static class SingletonHolder {
        private static final BoxFactory INSTANCE = new BoxFactory();
    }

    public AbstractBox getBox(String key) {
        returnmap.get(key); }}Copy the code

Advantages and disadvantages and usage scenarios

advantages

  • Greatly reduces the number of similar or identical objects in memory, saving system resources and improving system performance
  • The external state in the share mode is relatively independent and does not affect the internal state

Disadvantages:

In order to make the object can be shared, it is necessary to externalize part of the state of the shared object, separate the internal state from the external state, and make the program logic complicated

Usage Scenarios:

  • A system has a large number of identical or similar objects, resulting in a large memory consumption.
  • Most of the state of an object can be externalized, and these external states can be passed into the object.
  • The use of the share pattern requires maintaining a pool of share objects, which requires a certain amount of system resources, so it should be worthwhile to use the share pattern only when the share objects need to be reused multiple times.

JDK source code

The Integer class uses the free element schema. Let’s start with the following example:

public class Demo {
    public static void main(String[] args) {
        Integer i1 = 127;
        Integer i2 = 127;

        System.out.println("Are i1 and i2 objects the same object?" + (i1 == i2));

        Integer i3 = 128;
        Integer i4 = 128;

        System.out.println("Are i3 and i4 objects the same object?"+ (i3 == i4)); }}Copy the code

Run the above code and the result is as follows:

Why is the first output true and the second false? Decompiler by decompiler software, the code is as follows:

public class Demo {
    public static void main(String[] args) {
        Integer i1 = Integer.valueOf((int)127);
        Integer i2 Integer.valueOf((int)127);
        System.out.println((String)new StringBuilder().append((String)"i1\u548ci2\u5bf9\u8c61\u662f\u5426\u662f\u540c\u4e00\u4e2a\u5bf9\u8c61\uff1f").append((boolean)(i1 == i2)).toString());
        Integer i3 = Integer.valueOf((int)128);
        Integer i4 = Integer.valueOf((int)128);
        System.out.println((String)new StringBuilder().append((String)"i3\u548ci4\u5bf9\u8c61\u662f\u5426\u662f\u540c\u4e00\u4e2a\u5bf9\u8c61\uff1f").append((boolean)(i3 == i4)).toString()); }}Copy the code

As you can see from the code above, assigning directly to a variable of type Integer to a primitive data type uses valueOf() underneath, so you just need to look at this method

public final class Integer extends Number implements Comparable<Integer> {
    
	public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }
    
    private static class IntegerCache {
        static final int low = -128;
        static final int high;
        static final Integer cache[];

        static {
            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) {
                }
            }
            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;
        }

        private IntegerCache(a) {}}}Copy the code

If valueOf is between -128 and 127, the subscript is calculated and returned from the cache. If valueOf is between -128 and 127, a new Integer object is created.