The of Java design pattern -- meta pattern

1. What is the meta model?

Use sharing to support large numbers of fine-grained objects efficiently.

Flyweight Pattern: using shared objects can effectively support a large number of fine-grained objects.

Human words: reuse objects and save memory.

2. Meta mode definition

① , Flyweight - Abstract meta role

Is an abstract class of a product, which defines the interface or implementation of the external state and internal state of an object at the same time.

An object information can be divided into internal state and external state.

Internal status: the information that can be shared by the object is stored in the shared meta object and will not change with the environment. It can be used as the dynamic additional information of an object. It does not need to be directly stored in a specific object. It belongs to the part that can be shared.

External state: a mark on which an object can depend. It is a state that changes with the environment and cannot be shared.

② , ConcreteFlyweight - specific roles

A specific product class that implements the business defined by the abstract role. In this role, it should be noted that the internal state processing should be independent of the environment. There should be no operation that changes the internal state and modifies the external state. This is absolutely not allowed.

③ , unsharedConcreteFlyweight -- unshareable meta roles

Objects with no external state or security requirements (such as thread safety) that cannot use sharing technology generally do not appear in the sharing factory.

④ , FlyweightFactory - Xiangyuan factory

The responsibility is very simple. It is to construct a pool container and provide methods to get objects from the pool.

3. Shared meta mode common code

/**
 * Abstract meta role
 */
public abstract class Flyweight {
    // Internal state
    private String instrinsic;

    // External status can be modified through final to prevent modification
    protected final String extrinsic;

    protected Flyweight(String extrinsic) {
        this.extrinsic = extrinsic;
    }

    // Define business operations
    public abstract void operate();

    public String getInstrinsic() {
        return instrinsic;
    }

    public void setInstrinsic(String instrinsic) {
        this.instrinsic = instrinsic;
    }
}
/**
 * Specific meta role 1
 */
public class ConcreteFlyweight1 extends Flyweight{

    protected ConcreteFlyweight1(String extrinsic) {
        super(extrinsic);
    }

    @Override
    public void operate() {
        System.out.println("Specific meta role 1");
    }
}
/**
 * Specific meta role 2
 */
public class ConcreteFlyweight2 extends Flyweight{

    protected ConcreteFlyweight2(String extrinsic) {
        super(extrinsic);
    }

    @Override
    public void operate() {
        System.out.println("Specific meta role 2");
    }
}
public class FlyweightFactory {
    // Define a pool container
    private static HashMap<String,Flyweight> pool = new HashMap<>();

    // Xiangyuan factory
    public static Flyweight getFlyweight(String extrinsic){
        // Object to return
        Flyweight flyweight = null;
        // The object does not exist in the pool
        if(pool.containsKey(extrinsic)){
            flyweight = pool.get(extrinsic);
        }else{
            // Create meta objects based on external state
            flyweight = new ConcreteFlyweight1(extrinsic);
            // Place in pool
            pool.put(extrinsic,flyweight);
        }
        return flyweight;
    }
}

4. Through meta design text editor

Suppose that the text editor only contains text editing function, and only records text and format information, including font model, size, color and other information of text.

4.1 General implementation

Usually design is to treat each text as a separate object.

package com.itcoke.designpattern.flyweight.edittext;

/**
 * Single text object
 */
public class Character {
    // character
    private char c;
    // Font model
    private String font;
    // font size
    private int size;
    // Font color
    private int colorRGB;

    public Character(char c, String font, int size, int colorRGB){
        this.c = c;
        this.font = font;
        this.size = size;
        this.colorRGB = colorRGB;
    }

    @Override
    public String toString() {
        return String.valueOf(c);
    }
}

/**
 * Editor implementation
 */
public class Editor {
    private ArrayList<Character> chars = new ArrayList<>();

    public void appendCharacter(char c, String font, int size, int colorRGB){
        Character character = new Character(c,font,size,colorRGB);
        chars.add(character);
    }

    public void display(){
        System.out.println(chars);
    }
}

client:

public class EditorClient {
    public static void main(String[] args) {
        Editor editor = new Editor();
        editor.appendCharacter('A',"Song typeface",11,0XFFB6C1);
        editor.appendCharacter('B',"Song typeface",11,0XFFB6C1);
        editor.appendCharacter('C',"Song typeface",11,0XFFB6C1);
        editor.display();
    }
}

4.2 rewriting of sharing mode

The above problem is easy to find. Each Character will create a Character object. If it is millions of characters, there will be millions of objects in memory. How to save these memory?

In fact, after analysis, there are usually not many font formats, so we can set the font format to shared element, that is, the internal state that can be shared as mentioned above.

Internal status (shared): font type, size, color

External status (not shared): characters

So the code is rewritten as follows:

public class CharacterStyle {
    // Font model
    private String font;
    // font size
    private int size;
    // Font color
    private int colorRGB;

    public CharacterStyle(String font, int size, int colorRGB) {
        this.font = font;
        this.size = size;
        this.colorRGB = colorRGB;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        CharacterStyle that = (CharacterStyle) o;
        return size == that.size &&
                colorRGB == that.colorRGB &&
                Objects.equals(font, that.font);
    }

    @Override
    public int hashCode() {
        return Objects.hash(font, size, colorRGB);
    }
}
public class CharacterStyleFactory {

    private static final Map<CharacterStyle,CharacterStyle> mapStyles = new HashMap<>();

    public static CharacterStyle getStyle(String font, int size, int colorRGB){
        CharacterStyle newStyle = new CharacterStyle(font,size,colorRGB);

        if(mapStyles.containsKey(newStyle)){
            return mapStyles.get(newStyle);
        }
        mapStyles.put(newStyle,newStyle);
        return newStyle;
    }
}
public class Character {
    private char c;
    private CharacterStyle style;

    public Character(char c, CharacterStyle style) {
        this.c = c;
        this.style = style;
    }

    @Override
    public String toString() {
        return String.valueOf(c);
    }
}
public class Editor {
    private List<Character> chars = new ArrayList<>();

    public void appendCharacter(char c, String font, int size, int colorRGB){
        Character character = new Character(c,CharacterStyleFactory.getStyle(font,size,colorRGB));
        chars.add(character);
    }

    public void display(){
        System.out.println(chars);
    }
}

5. Application of meta pattern in java.lang.Integer

Look at the following code. What is the print result?

public class IntegerTest {
    public static void main(String[] args) {
        Integer i1 = 56;
        Integer i2 = 56;
        Integer i3 = 129;
        Integer i4 = 129;
        System.out.println(i1 == i2); 
        System.out.println(i3 == i4); 
    }
}

Why is this result?

First, Integer i = 59; The underlying layer executes: Integer i = Integer.valueOf(59); This is automatic packing.

int j = i; The underlying layer executes: int j = i.intValue(); This is automatic unpacking.

Then we the Integer.valueOf() method:

Look at the source code of IntegerCache:

   private static class IntegerCache {
        static final int low = -128;
        static final int high;
        static final Integer cache[];

        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;
        }

        private IntegerCache() {}
    }

In fact, this is the factory class of meta object we mentioned earlier. It caches integer values between - 128 and 127, which are the most commonly used integer values. Of course, JDK also provides methods to allow us to customize the maximum value of cache.

6. Enjoy the advantages of meta mode

Reduce the objects created by the application, reduce the occupation of program memory, and enhance the performance of the program.

However, it also improves the complexity of the system. It needs to separate the external state from the internal state. Moreover, the external state has solidification characteristics and should not change with the change of the internal state, otherwise it will lead to the logical confusion of the system.

7. Application scenario of meta mode

① There are a large number of similar objects in the system.

② Both and fine-grained objects have close external states, and the internal state is independent of the environment, that is, the object has no specific identity.

③ . scenarios requiring buffer pool.

Tags: Design Pattern

Posted on Mon, 29 Nov 2021 21:38:48 -0500 by bob1660