Flyweight Patterns

享元模式,这俩字是真不知道谁想出来的

GoF定义:使用共享来高效支持大量细粒度的对象

概念

一个flyweight是一个对象,通过这个对象我们使用尽可能多的共享数据来节省内存。这里有两个术语,intrinsic_和_extrinsic,前者可以被存在flyweight中并且是可共享的,后者依赖flyweight的上下文并且是不可共享的,客户端对象需要把extrinsic传入flyweight

例子

现实世界:在实际的企业应用中,我们不希望存储很多相同的对象,这个模式的概念就是应用在这个场景下的。(这里的例子是,两个人租房子,同时看到一个房子(设施齐全,但是只有一套,并且价格昂贵),最后他们选择合租这个房子(即共享))
代码世界:文字处理器中,字符的图像化显示。多人游戏中的玩家,虽然看起来是一样的,但是他们的一些属性是不同的

展示

例中,如果已经创建过同样类型的对象,那么不会再创建新的对象

代码

public class FlyweightPatternEx
{
    public static void main(String[] args) throws Exception
    {
        RobotFactory myfactory = new RobotFactory();
        System.out.println("\n***Flyweight Pattern Example***\n");
        IRobot shape = myfactory.getRobotFromFactory("small");
        shape.print();
        /*Here we are trying to get the objects additional 2 times. Note that from now onward we do not need to create additional small robots as we have already created this category*/
        for (int i = 0; i < 2; i++)
        {
            shape = myfactory.getRobotFromFactory("small");
            shape.print();
        }
        int NumOfDistinctRobots = myfactory.totalObjectsCreated();
        System.out.println("\nDistinct Robot objects created till now= "+ NumOfDistinctRobots);
        /*Here we are trying to get the objects 5 times. Note that the second time onward we do not need to create additional large robots as we have already created this category in the first attempt(at i=0)*/
        for (int i = 0; i < 5; i++)
        {
            shape = myfactory.getRobotFromFactory("big");
            shape.print();
        }

        NumOfDistinctRobots = myfactory.totalObjectsCreated();
        System.out.println("\nFinally no of Distinct Robot objects created: "+ NumOfDistinctRobots);
    }
}

interface IRobot
{
    void print();
}

class SmallRobot implements IRobot
{
    @Override
    public void print()
    {
        System.out.println("This is a Small Robot");
    }
}

class BigRobot implements IRobot
{
    @Override
    public void print()
    {
        System.out.println("This is a Big Robot");
    }
}

class RobotFactory
{
    Map<String, IRobot> shapes = new HashMap<>(16);

    public int totalObjectsCreated()
    {
        return shapes.size();
    }

    public IRobot getRobotFromFactory(String robotCategory) throws Exception
    {
        IRobot robot= null;

        if (shapes.containsKey(robotCategory)) {
            robot = shapes.get(robotCategory);
        } else {
            switch (robotCategory) {
                case "small":
                    System.out.println("We do not have Small Robot. So we are creating a Small Robot now.");
                    robot = new SmallRobot();
                    shapes.put("small", robot);
                    break;
                case "big":
                    System.out.println("We do not have Large Robot. So we are creating a Large Robot now .");
                    robot = new BigRobot();
                    shapes.put("big", robot);
                    break;
                default:
                    throw new Exception("Robot Factory can create only small and large shapes");
            }
        }

        return robot;
    }
}

改进

上例中的代码很像单例模式,因为相同类型的对象只创建过一次。但是有可能对于某个类型的对象,我们只需要它的基本结构,并且还要再加一些特性。接下来的例子中,我们处理可以是king或者queen类型的robot,如果我们已经有了king或者queen类型的robot,那么不会再创建新的对象。我们会在工厂中先获取robot的基本结构,然后再给它们上颜色(多了颜色属性)

展示

代码

public class FlyweightImprovePatternEx
{
    public static void main(String[] args) throws Exception
    {
        ColoredRobotFactory myfactory = new ColoredRobotFactory();
        System.out.println("\n***Flyweight Pattern Example Modified***\n"); Robot shape;
        /*Here we are trying to get 3 king type robots*/
        for (int i = 0; i < 3; i++)
        {
            shape =(Robot) myfactory.getRobotFromFactory("King");
            shape.setColorOfRobot(getRandomColor());
            shape.print();
        }
        /*Here we are trying to get 3 queen type robots*/ for (int i = 0; i < 3; i++)
        {
            shape =(Robot) myfactory.getRobotFromFactory("Queen");
            shape.setColorOfRobot(getRandomColor());
            shape.print();
        }
        int NumOfDistinctRobots = myfactory.totalObjectCreated();
        //System.out.println("\nDistinct Robot objects created till now = "+ NumOfDistinctRobots);
        System.out.println("\n Finally no of Distinct Robot objects created: "+ NumOfDistinctRobots);
    }

    public static String getRandomColor()
    {
        Random r = new Random();
        /*You can supply any number of your choice in nextInt argument.
        * we are simply checking the random number generated is an even number
        * or an odd number. And based on that we are choosing the color. For
        simplicity, we'll use only two colors—red and green
        */
        int random = r.nextInt(20);
        if (random % 2 == 0)
        {
            return "red";
        }
        else
        {
            return "green";
        }
    }
}

interface IIRobot
{
    void print();
}

class Robot implements IIRobot
{
    private String typeOfRobot;
    private String colorOfRobot;

    public Robot(String typeOfRobot)
    {
        this.typeOfRobot = typeOfRobot;
    }

    public void setColorOfRobot(String colorOfRobot)
    {
        this.colorOfRobot = colorOfRobot;
    }

    @Override
    public void print()
    {
        System.out.println("This is a " + typeOfRobot + " type robot with "+ colorOfRobot + "color");
    }
}

class ColoredRobotFactory
{
    Map<String, IIRobot> shapes = new HashMap<>(16);

    public int totalObjectCreated()
    {
        return shapes.size();
    }

    public IIRobot getRobotFromFactory(String type) throws Exception
    {
        IIRobot robot = null;
        if (shapes.containsKey(type))
        {
            robot = shapes.get(type);
        }
        else {
            switch (type) {
                case "King":
                    System.out.println("We do not have King Robot. So we are creating a King Robot now.");
                    robot = new Robot("King");
                    shapes.put("King",robot);
                    break;
                case "Queen":
                    System.out.println("We do not have Queen Robot. So we are creating a Queen Robot now.");
                    robot = new Robot("Queen");
                    shapes.put("Queen",robot);
                    break;
                default:
                    throw new Exception("Robot Factory can create only king and queen type robots");
            }
        }

        return robot;
    }
}

Note

  1. 节省内存是这个模式的核心,即flyweight越多,我们节省的内存就越多
  2. 如果我们可以计算出extrinsic的状态而不是存储他们,那么可以节省的内存更多
  3. 有时在树形结构中,我们为了共享子节点,可以使用这个模式结合组合模式(composite pattern)
  4. flyweight接口可能是共享的,也可能不是,在后面这种情况下,一般它的子类是具体(concrete)的flyweight
  5. 简单来说,intrinsic的状态让对象保持唯一,而extrinsic的状态是通过参数传递的

思考

这个模式的核心就如上所述,是为了节省内存而出现的,即用CPU的计算时间来节省内存的空间。感觉如果不是特殊限制条件,一般不会专门用这个模式。但是节省内存的这种共享对象思想是可以运用在编码过程中的

posted on 2020-12-07 22:05  老鼠不上树  阅读(73)  评论(0)    收藏  举报