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
- 节省内存是这个模式的核心,即flyweight越多,我们节省的内存就越多
- 如果我们可以计算出extrinsic的状态而不是存储他们,那么可以节省的内存更多
- 有时在树形结构中,我们为了共享子节点,可以使用这个模式结合组合模式(composite pattern)
- flyweight接口可能是共享的,也可能不是,在后面这种情况下,一般它的子类是具体(concrete)的flyweight
- 简单来说,intrinsic的状态让对象保持唯一,而extrinsic的状态是通过参数传递的
思考
这个模式的核心就如上所述,是为了节省内存而出现的,即用CPU的计算时间来节省内存的空间。感觉如果不是特殊限制条件,一般不会专门用这个模式。但是节省内存的这种共享对象思想是可以运用在编码过程中的