The flyweight pattern

I. What is the yuan model

Object orientation solves some flexibility or extensibility problems well, but in many cases it increases the number of classes in the system. The increasing number of classes in the system will affect the performance of the program. The share mode improves the utilization rate of system resources by using the sharing technology to improve the reuse of the same or similar objects.

Definition of Flyweight pattern: Sharing techniques are used to efficiently support reuse of large numbers 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, thus improving the utilization rate of system resources.

The share pattern has two requirements: fineness and shared objects. With fine granularity, it is inevitable that there will be many objects with similar properties, so we divide the information about these objects into two parts: internal state and external state.

The Intrinsic State is the same as that of the insic mode, while Extrinsic State is the same as that of the external environment. The external State and internal State are independent. A change in the external state does not cause a change in the internal state. Because internal and external states are distinguished, different external states can be set so that the same object can have some different characteristics, and the same internal state can be shared. In other words, the essence of the sharing pattern is separation and sharing: separation of change and immutability, and sharing immutability.

Do you understand the internal and external states above? Let’s first understand internal states and external states. For example: I am Zhang SAN. Everyday go to work to come home. Internal state is: my invariable attribute, I call zhang SAN. The external state is: I have to go to work, go home, the change of geographical location, for example: play go. Go has black and white pieces. The internal states are: the fixed properties of Go, the colors of the pieces, and the spots/white pieces. The external states are: the change of the coordinates of the positions on the board. The user name, password, connection URL and other information stored in the connection object are set when the object is created and do not change with the environment. The external state is: When each connection is recycled, we need to mark it as available. These are the external states. Name and go black and white, connection pool connection information is a fixed property. They are all internal states. And the external state is changing. Like this, there’s a distinction between internal state and external state, so we can treat internal state as part of the share and the essence of the share pattern is to cache shared objects and reduce memory consumption.

Try to understand the above paragraph again.

  1. The Intrinsic State is the same content that can be shared with the external environment, while the changes that cannot be shared with the external environment are Extrinsic State. Here go color, people’s names are not changing, they can be used as an internal state. The change of position, the change of coordinate is changed with the change of environment scene, is the external state.

  2. The external state and the internal state are independent of each other, and the change of the external state does not cause the change of the internal state. If the internal state changes with the external state. That internal state is not shareable. For example, water becomes ice and becomes steam. After this external state changes, the internal state changes too, and the share mode cannot be used

  3. Because internal and external states are distinguished, different external states can be set so that the same object can have some different characteristics, and the same internal state can be shared. In other words, the essence of the sharing pattern is separation and sharing: separation of change and immutability, and sharing immutability.

    This sentence is a summary of the above. Use internal state as a shared object. The essence of share mode is to separate internal and external objects and share internal objects.

    The factory pattern is typically found in the share pattern, where a share factory needs to be created to maintain a Flyweight Pool (for storing share objects with the same internal state).

    In share mode, the internal state of the share object is shared, and the external state needs to be set by the environment. In practice, the number of internal states that can be shared is limited, so share objects are typically designed to be smaller objects that contain fewer internal states, also known as fine-grained objects.

    The purpose of the share pattern is to reuse a large number of fine-grained objects using sharing technology.

The main problem solved by the share mode is that when there are a large number of objects, it may cause memory overflow, so we abstract out the common part. If there is the same business request, we directly return the existing objects in memory, avoiding re-creation.

Two. Enjoy the yuan mode when to use

2. These objects can be divided into internal states and external states. These objects can be divided into many groups according to internal states.

Case 3.

We’re going to draw 20 circles with five colors. Each circle has a random color and coordinates.

1. General implementation.

Step 1: Define a shape interface

package com.lxl.www.designPatterns.flyweight.circle;

/** * shape */
public interface Shape {

    void draw(a);
}
Copy the code

Step 2: Define the circular implementation class

package com.lxl.www.designPatterns.flyweight.circle;

public class Circle implements Shape{

    /** ** color */
    private String color;
    /** ** x coordinates */
    private int x;

    /** ** y coordinate */
    private int y;

    /** * radius */
    private int radius;

    public Circle(String color, int x, int y, int radius) {
        this.color = color;
        this.x = x;
        this.y = y;
        this.radius = radius;
    }

    public void draw(a) {
        System.out.println("Center: {" + x + "," + y + }, radius: + radius);
    }

    public int getX(a) {
        return x;
    }

    public void setX(int x) {
        this.x = x;
    }

    public int getY(a) {
        return y;
    }

    public void setY(int y) {
        this.y = y;
    }

    public int getRadius(a) {
        return radius;
    }

    public void setRadius(int radius) {
        this.radius = radius; }}Copy the code

Step 3: Create 20 circles randomly in 5 colors

package com.lxl.www.designPatterns.flyweight.circle;

public class client {
    private static final String colors[] =
            { "Red"."Green"."Blue"."White"."Black" };

    private static String getRandomColor(a) {
        return colors[(int)(Math.random()*colors.length)];
    }
    private static int getRandomX(a) {
        return (int)(Math.random()*100 );
    }
    private static int getRandomY(a) {
        return (int)(Math.random()*100);
    }

    private static int getRadius(a) {
        return (int)(Math.random()*10+1);
    }


    public static void main(String[] args) {
        for (int i = 0; i < 20; i++) {
            Shape circle = newCircle(getRandomColor(), getRandomX(), getRandomY(), getRadius()); circle.draw(); }}}Copy the code

Running results:

49, circle: {86}, radius: 8 circle: {97, 15}, radius: 5 circle: {63} 38, radius: 9 circle: {}, 76, 43, radius: 2 circle: {} 98, 47, radius: 7 circle: {2, 96}, radius: 58, 3 circle: {86}, radius: 3 circle: {64} 83, radius: 4 circle: {56, 21}, radius: 7 circle: {57, 77}, radius: 3 circle: 29} {40, and radius: 9 circle: {23, 42}, radius: Radius of circle: {89, 35}, : 3 circle: {50, 16}, radius: 4 circle: {65, 9}, radius: 5 circle: {33, 9}, radius: 7 circle: {27, 82}, radius: 9 circle: {93} 97, radius: 1 Center: {30, 99}, radius: 3 Center: {17, 18}, radius: 4Copy the code

Analysis: This is the crude way, we see to draw 20 circles, created 20 objects. If YOU draw 100 circles you have to create 100 objects. If you play Go, there are many more objects to create. The system can’t handle that.

Next, we use the share pattern optimization

2. Enjoy yuan mode optimization

Step 1: Define the shape interface

package com.lxl.www.designPatterns.flyweight.flyweightCicle;

/** * shape interface */
public interface Shape {
   / * * * * /
   void draw(a);
}

Copy the code

Step 2: Define the prototype implementation class

package com.lxl.www.designPatterns.flyweight.flyweightCicle;

/** * circular implementation class */
public class Circle implements Shape{

    /** * the color of the circle */
    private String color;

    /** ** x coordinates */
    private int x;

    /** ** y coordinate */
    private int y;

    /** * radius */
    private int radius;

    / * * * * /
    @Override
    public void draw(a) {
        System.out.println("Center: {" + x + "," + y + }, radius: + radius);
    }

    public Circle(String color) {
        this.color = color;
    }

    public Circle(String color, int x, int y, int radius) {
        this.color = color;
        this.x = x;
        this.y = y;
        this.radius = radius;
    }

    public String getColor(a) {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    public int getX(a) {
        return x;
    }

    public void setX(int x) {
        this.x = x;
    }

    public int getY(a) {
        return y;
    }

    public void setY(int y) {
        this.y = y;
    }

    public int getRadius(a) {
        return radius;
    }

    public void setRadius(int radius) {
        this.radius = radius; }}Copy the code

Step 3: Enjoy yuan factory

package com.lxl.www.designPatterns.flyweight.flyweightCicle;

import java.util.HashMap;
import java.util.Map;

/** ** factory */
public class ShapeFactory {

    public static Map<String, Shape> shapeMap = new HashMap<>();

    public static Shape createShape(String color) {
        Shape shape = shapeMap.get(color);
        if (shape == null) {
            shape = new Circle(color);
            shapeMap.put(color, shape);
        }

        returnshape; }}Copy the code

In the meta factory, we define a Map collection where key is color and value is Shape. To create a shape, pass in a color and go to the map collection to get a color object. If there is one, fetch it. If there is none, create one and put it into the map.

Step 4: Client call

package com.lxl.www.designPatterns.flyweight.flyweightCicle;

public class Client {
    public static String[] colors = new String[] {"red"."blue"."green"."purple"."yellow"};

    private static String getRandomColor(a) {
        return colors[(int)(Math.random()*colors.length)];
    }
    private static int getRandomX(a) {
        return (int)(Math.random()*100 );
    }
    private static int getRandomY(a) {
        return (int)(Math.random()*100);
    }

    private static int getRadius(a) {
        return (int)(Math.random()*10+1);
    }

    public static void main(String[] args) {
        for (int i=0; i < 20; i++) { Circle circle = (Circle)ShapeFactory.getShape(getRandomColor()); circle.setX(getRandomX()); circle.setY(getRandomY()); circle.setRadius(getRadius()); circle.draw(); }}}Copy the code

How many objects have been created? You have a few colors and you create a few objects. Create a maximum of five objects. This greatly reduces the number of objects compared to the original 20. If go has only black and white pieces, only two objects need to be created.

Step 5: Run results

Circle: {10} 25, radius: 5 circle: {99} 74, radius: 8 circle: {13, 59}, radius: 1 circle: {62, 58}, radius: 6 circle: {98} 97, radius: 9 circle: 44}, {1, radius: 8 Center: {26, 97}, radius: 10 Center: {27, 68}, radius: 8 Center: {33, 68}, radius: 7 Center: {69, 23}, radius: 9 Center: {93, 27}, radius: 6 Center: {} 72, 90, radius: 1 circle: {61} 80, radius: 2 circle: {36, 88}, radius: 3 circle: {79} 8, radius: 5 circle: {87} 29, radius: 6 circle: {48, 62}, radius: 9 circle: {7, 71}, radius: 1 circle: {0, 67}, radius: 7 circle: 22} {, 59, radius: 2Copy the code

Summary: In this, Shape is the abstract interface of the element, Circle is the object of the element, and there are two parts in the Circle object, one is the state that can be shared, namely: color; The other part is the unshareable state, namely: center + radius. These two parts combine to form the share object.

Fourthly, enjoy the structure of yuan mode

1. Summary of Enjoy Yuan model

Take a look at a UML diagram of the Meta-schema:

As you can see from the figure above, the share mode usually contains the following roles:

  1. FlyWeight abstract object: FlyWeight is the base class of all concrete meta-classes. It is the public interface that needs to be implemented by the specification of concrete meta-classes. The external state of non-shared elements is passed in the form of parameters through methods.
  2. Shareable share concrete object, ConcreteFlyWeight: Implements the abstract share docking interface, receiving external state
  3. Not to share the flyweight concrete object UnShareConcreteFlyWeight: is not can share external state, it takes the form of parameters into specific flyweight relevant methods
  4. The FlyWeightFactory is responsible for creating and managing weightweight 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.
  5. The Client calls the Client

Enjoy the source code of the yuan model

Step 1: Non-shareable share element objects

package com.lxl.www.designPatterns.flyweight.demo;

/** * Unshareable share object * Here is the external state in the share object. The center and radius of the circle */
public class UnShareConcreteFlyWeight {
    /** * you can define any object in this file, and name it info just for illustration */
    private String info;

    public UnShareConcreteFlyWeight(String info) {
        this.info = info;
    }

    @Override
    public String toString(a) {
        return "UnShareConcreteFlyWeight{" +
                "info='" + info + '\' ' +
                '} ';
    }

    public String getInfo(a) {
        return info;
    }

    public void setInfo(String info) {
        this.info = info; }}Copy the code

The non-shareable share object, that is, the part of the external state that changes

Step 2: Enjoy the abstract interface

package com.lxl.www.designPatterns.flyweight.demo;

/** ** interface */
public interface FlyWeight {
    /** * Share objects have different behavior results due to unshareable state *@param unshare
     */
    void operate(UnShareConcreteFlyWeight unshare);
}

Copy the code

The share interface defines a operate method, a method that changes with external state

Step 3: shareable share element object, implement share element abstract interface

package com.lxl.www.designPatterns.flyweight.demo;

/** * Specifies the share object */
public class ConcreteFlyWeight implements FlyWeight{

    /** * Internal state * Share objects can be grouped according to internal state */
    private String innerState;

    /** * Each object has an internal state when it is created *@param innerState
     */
    public ConcreteFlyWeight(String innerState) {
        this.innerState = innerState;
    }

    @Override
    public void operate(UnShareConcreteFlyWeight unshare) {
        System.out.println("The shared state is :"+ innerState + ", the unshared state is:" + unshare.toString());
    }

    public String getInnerState(a) {
        return innerState;
    }

    public void setInnerState(String innerState) {
        this.innerState = innerState;
    }

    @Override
    public String toString(a) {
        return "ConcreteFlyWeight{" +
                "innerState='" + innerState + '\' ' +
                '} '; }}Copy the code

This defines a shareable state that is specific to the object itself. The operate method of the parent class is also overridden.

Step 4: Enjoy yuan factory

package com.lxl.www.designPatterns.flyweight.demo;

import java.util.HashMap;

public class FlyWeightFactory {
    private HashMap<String, FlyWeight> flyweights = new HashMap<String, FlyWeight>();
    public FlyWeight getFlyweight(String key) {
        FlyWeight flyweight = (FlyWeight) flyweights.get(key);
        if(flyweight ! =null) {
            System.out.println("Specific Share" + key + "Already exists, successfully retrieved!");
        } else {
            System.out.println("Specific Share" + key + "Doesn't exist, create one!");
            flyweight = new ConcreteFlyWeight(key);
            flyweights.put(key, flyweight);
        }
        returnflyweight; }}Copy the code

A map defined by a share factory. Key is a share object and value is a share object. Objects of the same internal state can then share an object.

Step 5: Enjoy yuan client

package com.lxl.www.designPatterns.flyweight.demo;

public class FlyWeightClient {
    public static void main(String[] args) {
        FlyWeightFactory factory = new FlyWeightFactory();
        FlyWeight f01 = factory.getFlyweight("key1");
        FlyWeight f02 = factory.getFlyweight("key1");
        FlyWeight f03 = factory.getFlyweight("key1");
        FlyWeight f11 = factory.getFlyweight("key2");
        FlyWeight f12 = factory.getFlyweight("key2");
        f01.operate(new UnShareConcreteFlyWeight("First call to key1"));
        f02.operate(new UnShareConcreteFlyWeight("Second call to key1"));
        f03.operate(new UnShareConcreteFlyWeight("Third call to key1"));
        f11.operate(new UnShareConcreteFlyWeight("First call to key2"));
        f12.operate(new UnShareConcreteFlyWeight("Second call to key2")); }}Copy the code

Running results:

Key1 does not exist, create one! Key1 already exists, obtained successfully! Key1 already exists, obtained successfully! Key2 does not exist, create one! Key2 already exists, obtained successfully! UnShareConcreteFlyWeight{info=' 1st call to key1'} The shared state is key1, the unshared state is: UnShareConcreteFlyWeight{info=' 2nd call to key1'} Share status :key1, unshare status: UnShareConcreteFlyWeight{info=' 3rd call to key1'} Share status :key2, unshare status: UnShareConcreteFlyWeight{info=' 1st call to key2'} Shared status :key2, unshared status: UnShareConcreteFlyWeight{info=' 2nd call to key2'}Copy the code

Five. Enjoy the type of yuan mode

There are two types of share mode: pure share mode and composite share mode

1. Single storage mode

What we are talking about above is pure enjoy yuan mode, here will not repeat. Next, look at the composite share pattern.

2. Composite share mode

Composite enjoy yuan pattern, difficult to understand. Be sure to match a case