[Java Design Pattern]——Overview and examples of flyweight pattern

Overview

During object-oriented programming, you are sometimes faced with the problem of creating a large number of instances of the same or similar objects. Creating so many objects will consume a lot of system resources, which is a bottleneck in improving system performance.

For example, the black and white chess pieces in Go and Gobang, the coordinate points or colors in images, routers, switches and hubs in local area networks, tables and stools in classrooms, etc. These objects have many similarities. If their identical parts can be extracted and shared, a large amount of system resources can be saved. This is the background of the Flyweight pattern.

The definition and characteristics of flyweight mode

Definition of Flyweight mode: As the name suggests, it uses the characteristics of different variables in the JVM to store the same address and operate on the same memory space to implement object sharing technology to effectively support the reuse of a large number of fine-grained objects. It greatly reduces the number of objects that need to be created by sharing existing objects and avoids the overhead of a large number of similar classes, thereby improving the utilization of system resources.

The main advantage of the flyweight mode is that only one copy of the same object is saved, which reduces the number of objects in the system, thereby reducing the pressure on memory caused by fine-grained objects in the system.

Its main disadvantages are:

  1. In order to make objects shareable, some states that cannot be shared need to be externalized , which will increase the complexity of the program.
  2. Reading the external state of flyweight mode will make the running time slightly longer.

The structure and implementation of flyweight pattern

The definition of flyweight pattern puts forward two requirements, fine-grained and shared objects. Because fine-grainedness is required, it is inevitable that there will be a large number of objects with similar properties. At this time, we divide the information of these objects into two parts: internal state and external state.

  • Internal state refers to the information shared by the object, which is stored inside the flyweight information and does not change with changes in the environment;
  • External state refers to a mark that an object relies on, changes with changes in the environment, and cannot be shared.

For example, the connection object in the connection pool, the user name, password, connection URL and other information stored in the connection object are set when the object is created and will not change with changes in the environment. These are internal states. And when each connection is to be recycled, we need to mark it as available. These are external states.

The essence of flyweight mode is to cache shared objects and reduce memory consumption.

1. Structure of the pattern

The main roles of the flyweight model are as follows:

  1. Abstract flyweight role (Flyweight): It is the base class of all concrete flyweight classes. It is the public interface that needs to be implemented by the specific flyweight specification. Non-flyweight external states are passed in through methods in the form of parameters.
  2. Concrete Flyweight role: implements the interface specified in the abstract Flyweight role.
  3. Unsharable Flyweight role: It is an external state that cannot be shared. It is injected into specific flyweight related methods in the form of parameters.
  4. Flyweight Factory role: responsible for creating and managing flyweight roles. When a client object requests a flyweight object, the flyweight factory checks whether a flyweight object that meets the requirements exists in the system. If it exists, it will be provided to the client; if it does not exist, a new flyweight object will be created.

Figure 1 is the structure diagram of the flyweight mode, where:

  • UnsharedConcreteFlyweight is a non-flyweight role, which contains non-shared external state information info;
  • Flyweight is an abstract flyweight role, which contains the flyweight method operation (UnsharedConcreteFlyweight state). Non-flyweight external states are passed in through this method in the form of parameters;
  • ConcreteFlyweight is a concrete flyweight role, including the keyword key, which implements the abstract flyweight interface;
  • FlyweightFactory is a flyweight factory role, which is the keyword key to manage specific flyweights;
  • The customer role obtains specific flyweights through the flyweight factory and accesses related methods of specific flyweights.

Insert image description here

2. Theoretical implementation of the model

The implementation code of flyweight mode is as follows:

public class FlyweightPattern {
    
    
    public static void main(String[] args) {
    
    
        FlyweightFactory factory = new FlyweightFactory();
        Flyweight f01 = factory.getFlyweight("a");
        Flyweight f02 = factory.getFlyweight("a");
        Flyweight f03 = factory.getFlyweight("a");
        Flyweight f11 = factory.getFlyweight("b");
        Flyweight f12 = factory.getFlyweight("b");
        f01.operation(new UnsharedConcreteFlyweight("第1次调用a。"));
        f02.operation(new UnsharedConcreteFlyweight("第2次调用a。"));
        f03.operation(new UnsharedConcreteFlyweight("第3次调用a。"));
        f11.operation(new UnsharedConcreteFlyweight("第1次调用b。"));
        f12.operation(new UnsharedConcreteFlyweight("第2次调用b。"));
    }
}

//非享元角色
class UnsharedConcreteFlyweight {
    
    
    private String info;

    UnsharedConcreteFlyweight(String info) {
    
    
        this.info = info;
    }

    public String getInfo() {
    
    
        return info;
    }

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

//抽象享元角色
interface Flyweight {
    
    
    public void operation(UnsharedConcreteFlyweight state);
}

//具体享元角色
class ConcreteFlyweight implements Flyweight {
    
    
    private String key;

    ConcreteFlyweight(String key) {
    
    
        this.key = key;
        System.out.println("具体享元" + key + "被创建!");
    }

    public void operation(UnsharedConcreteFlyweight outState) {
    
    
        System.out.print("具体享元" + key + "被调用,");
        System.out.println("非享元信息是:" + outState.getInfo());
    }
}

//享元工厂角色
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("具体享元" + key + "已经存在,被成功获取!");
        } else {
    
    
            flyweight = new ConcreteFlyweight(key);
            flyweights.put(key, flyweight);
        }
        return flyweight;
    }
}

The results of running the program are as follows:

具体享元a被创建!
具体享元a已经存在,被成功获取!
具体享元a已经存在,被成功获取!
具体享元b被创建!
具体享元b已经存在,被成功获取!
具体享元a被调用,非享元信息是:1次调用a。
具体享元a被调用,非享元信息是:2次调用a。
具体享元a被调用,非享元信息是:3次调用a。
具体享元b被调用,非享元信息是:1次调用b。
具体享元b被调用,非享元信息是:2次调用b。

Application examples of flyweight mode

In product flash sales, many stores participate at the same time, and the information of one product remains unchanged, but the inventory of different stores is different and will change accordingly.
At this time, the product information is an internal state and is unchanged; the inventory is an external state and can change.

Code

Abstract Shared Role – Commodity

public interface Commodity {
    
    

    /**
     * 售出
     * @param inventory
     */
    void sell(Inventory inventory);

}

Non-Shared Characters – Inventory

public class Inventory {
    
    

    /**
     * 商品库存
     */
    private int num ;

    public Inventory(int num) {
    
    
        this.num = num;
    }

    public int getNum() {
    
    
        return num;
    }

    public void setNum(int num) {
    
    
        this.num = num;
    }
}

Specific shared roles – mobile products

public class Phone implements Commodity {
    
    

    private String description;

    public Phone(String description) {
    
    
        this.description = description;
        System.out.println("商品 : " + this.description + " 被创建了。");
    }

    @Override
    public void sell(Inventory inventory) {
    
    
        int num = inventory.getNum();
        System.out.println(this.description + "将被售出 ,当前库存 : " + num);
        System.out.println("商品 : " + this.description + " 被售出。");
        inventory.setNum(--num);
        System.out.println("商品售出后库存 : " + num);
    }
}

Specific shared role – slippers

public class Slipper implements Commodity {
    
    

    private String description;

    public Slipper(String description) {
    
    
        this.description = description;
    }

    @Override
    public void sell(Inventory inventory) {
    
    
        int num = inventory.getNum();
        System.out.println(this.description + "将被售出 ,当前库存 : " + num);
        System.out.println("商品 : " + this.description + " 被售出。");
        inventory.setNum(--num);
        System.out.println("商品售出后库存 : " + num);
    }
}

Xiangyuan Factory

public class CommodityFactory {
    
    
    private Map<String, Commodity> commodityMap = new HashMap<String, Commodity>();

    public Commodity getCommodityInstance(String key) {
    
    
        if (!commodityMap.containsKey(key)) {
    
    
            if ("拖鞋".equals(key)) {
    
    
                commodityMap.put(key,new Slipper("超级拖鞋(航天品质)"));
            } else if ("手机".equals(key)) {
    
    
                commodityMap.put(key,new Phone("超级手机(聚变发电)"));
            } else {
    
    
                throw new RuntimeException("不存在的手机型号");
            }
        }
        return commodityMap.get(key);
    }
}

Entry function

public class AppMain {
    
    

    public static void main(String[] args) {
    
    
        CommodityFactory commodityFactory = new CommodityFactory();
        System.out.println("-------商品1-------");
        /**
         * 现有四家店铺,销售描述相同的手机,但每家点的库存不一样
         */
        // 店铺1
        Commodity phone1 = commodityFactory.getCommodityInstance("手机");
        phone1.sell(new Inventory(1000));
        // 店铺2
        Commodity phone2 = commodityFactory.getCommodityInstance("手机");
        phone2.sell(new Inventory(900));
        // 店铺3
        Commodity phone3 = commodityFactory.getCommodityInstance("手机");
        phone3.sell(new Inventory(800));
        // 店铺4
        Commodity phone4 = commodityFactory.getCommodityInstance("手机");
        phone4.sell(new Inventory(300));

        System.out.println("========校验是否是相同对象========");
        System.out.println("phone1 == phone2 ? " + (phone1 == phone2));
        System.out.println("phone2 == phone3 ? " + (phone2 == phone3));
        System.out.println("phone3 == phone4 ? " + (phone3 == phone4));

        System.out.println("-------商品2------测试略---");
        Commodity slipper = commodityFactory.getCommodityInstance("拖鞋");
        slipper.sell(new Inventory(10));
    }
}

Application scenarios of flyweight mode

When the same set of information is needed in multiple places in the system, the information can be encapsulated into an object and then cached. In this way, one object can be provided to multiple places that need to be used, avoiding a large number of the same object multiple times. Create and reduce the consumption of large amounts of memory space.

The flyweight pattern is actually an improved mechanism of the factory method pattern. The flyweight pattern also requires the creation of one or a group of objects, and the objects are generated through the factory method pattern. However, the flyweight pattern adds the cache function to the factory method pattern . .

The structure and characteristics of the flyweight pattern have been analyzed previously, and its applicable application scenarios will be analyzed below. Flyweight mode saves memory space by reducing the number of objects in memory, so the following situations are suitable for using flyweight mode.

  1. There are a large number of identical or similar objects in the system, and these objects consume a lot of memory resources.

  2. Most objects can be grouped according to their internal state, and different parts can be externalized so that each group only needs to save a single piece of internal state.

  3. Since the flyweight mode requires the maintenance of an additional data structure to store flyweights , it should be worthwhile to use the flyweight mode only when there are enough flyweight instances.

    Application of flyweight model in practical work

    In the flyweight pattern introduced earlier, the structure diagram usually contains parts that can be shared and parts that cannot be shared. In actual use, there are sometimes slight changes, that is, there are two special flyweight modes: simple flyweight mode and compound flyweight mode . They are briefly introduced below.

(1) Pure flyweight mode. All specific flyweight classes in this flyweight mode can be shared, and there are no non-shared specific flyweight classes. Its structure diagram is shown in Figure 4.
Insert image description here

(2) Composite flyweight mode . Some flyweight objects in this flyweight mode are composed of some simple flyweight objects. They are composite flyweight objects. Although composite flyweight objects themselves cannot be shared, they can be decomposed into simple flyweight objects and then shared.

If you want to set the same external state for multiple flyweight objects with different internal states (in the above product sales case, it can be understood as giving slippers**[You can add a new method show for slippers to distinguish it from mobile phones ]** Set the same inventory as the mobile phone respectively), you can consider using the composite flyweight mode.

Its structure diagram is shown in Figure 5.
Insert image description here

references

Design Pattern-C Language Chinese Website

Guess you like

Origin blog.csdn.net/qq_44778023/article/details/131033024