Java中的Flyweight设计模式
今天,我们将研究Flyweight设计模式。
轻量化设计模式
根据GoF,flyweight设计模式的意图是:
<p>Use sharing to support large numbers of fine-grained objects efficiently</p>
Flyweight设计模式是一种结构设计模式,例如Facade模式,Adapter模式和Decorator模式。
当我们需要创建一个类的许多对象时,可以使用Flyweight设计模式。
由于每个对象都会占用对于低内存设备(例如移动设备或者嵌入式系统)至关重要的内存空间,因此可以应用flyweight设计模式来通过共享对象来减少内存负载。
在应用flyweight设计模式之前,我们需要考虑以下因素:
应用程序要创建的对象数量应该很大。
对象的创建占用大量内存,并且也很耗时。
对象属性可以分为内部属性和外部属性,对象的外部属性应由客户端程序定义。
要应用flyweight模式,我们需要将Object属性分为内部属性和外部属性。
内部属性使对象唯一,而外部属性由客户端代码设置并用于执行不同的操作。
例如,对象圆可以具有外部属性,例如颜色和宽度。
为了应用flyweight模式,我们需要创建一个Flyweight工厂来返回共享对象。
对于我们的示例,假设我们需要使用线条和椭圆形创建图形。
因此,我们将有一个"形状"接口及其具体实现,如"线"和"椭圆"。
椭圆类将具有固有属性,以确定是否用给定的颜色填充椭圆,而Line将不具有任何固有属性。
Flyweight设计模式界面和具体程序
Shape.java
package com.theitroad.design.flyweight; import java.awt.Color; import java.awt.Graphics; public interface Shape { public void draw(Graphics g, int x, int y, int width, int height, Color color); }
Line.java
package com.theitroad.design.flyweight; import java.awt.Color; import java.awt.Graphics; public class Line implements Shape { public Line(){ System.out.println("Creating Line object"); //adding time delay try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } @Override public void draw(Graphics line, int x1, int y1, int x2, int y2, Color color) { line.setColor(color); line.drawLine(x1, y1, x2, y2); } }
椭圆形Java
package com.theitroad.design.flyweight; import java.awt.Color; import java.awt.Graphics; public class Oval implements Shape { //intrinsic property private boolean fill; public Oval(boolean f){ this.fill=f; System.out.println("Creating Oval object with fill="+f); //adding time delay try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } @Override public void draw(Graphics circle, int x, int y, int width, int height, Color color) { circle.setColor(color); circle.drawOval(x, y, width, height); if(fill){ circle.fillOval(x, y, width, height); } } }
注意,我在创建具体类的对象时故意引入了延迟,以使flyweight模式可用于实例化时花费大量时间的对象。
轻量化工厂
flyweight工厂将由客户端程序用来实例化Object,因此我们需要在工厂中保留一个Object映射,客户端应用程序不应访问该Map。
每当客户端程序调用以获取Object的实例时,都应从HashMap返回它,如果找不到,则创建一个新的Object并放入Map中,然后返回它。
我们需要确保在创建对象时考虑所有固有属性。
我们的flyweight工厂类看起来像下面的代码。
ShapeFactory.java
package com.theitroad.design.flyweight; import java.util.HashMap; public class ShapeFactory { private static final HashMap<ShapeType,Shape> shapes = new HashMap<ShapeType,Shape>(); public static Shape getShape(ShapeType type) { Shape shapeImpl = shapes.get(type); if (shapeImpl == null) { if (type.equals(ShapeType.OVAL_FILL)) { shapeImpl = new Oval(true); } else if (type.equals(ShapeType.OVAL_NOFILL)) { shapeImpl = new Oval(false); } else if (type.equals(ShapeType.LINE)) { shapeImpl = new Line(); } shapes.put(type, shapeImpl); } return shapeImpl; } public static enum ShapeType{ OVAL_FILL,OVAL_NOFILL,LINE; } }
注意在getShape方法中使用Java Enum进行类型安全,Java Composition(形状映射)和Factory模式。
Flyweight设计模式客户端示例
下面是使用flyweight模式实现的示例程序。
DrawingClient.java
package com.theitroad.design.flyweight; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Container; import java.awt.Graphics; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JPanel; import com.theitroad.design.flyweight.ShapeFactory.ShapeType; public class DrawingClient extends JFrame{ private static final long serialVersionUID = -1350200437285282550L; private final int WIDTH; private final int HEIGHT; private static final ShapeType shapes[] = { ShapeType.LINE, ShapeType.OVAL_FILL,ShapeType.OVAL_NOFILL }; private static final Color colors[] = { Color.RED, Color.GREEN, Color.YELLOW }; public DrawingClient(int width, int height){ this.WIDTH=width; this.HEIGHT=height; Container contentPane = getContentPane(); JButton startButton = new JButton("Draw"); final JPanel panel = new JPanel(); contentPane.add(panel, BorderLayout.CENTER); contentPane.add(startButton, BorderLayout.SOUTH); setSize(WIDTH, HEIGHT); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setVisible(true); startButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { Graphics g = panel.getGraphics(); for (int i = 0; i < 20; ++i) { Shape shape = ShapeFactory.getShape(getRandomShape()); shape.draw(g, getRandomX(), getRandomY(), getRandomWidth(), getRandomHeight(), getRandomColor()); } } }); } private ShapeType getRandomShape() { return shapes[(int) (Math.random() * shapes.length)]; } private int getRandomX() { return (int) (Math.random() * WIDTH); } private int getRandomY() { return (int) (Math.random() * HEIGHT); } private int getRandomWidth() { return (int) (Math.random() * (WIDTH/10)); } private int getRandomHeight() { return (int) (Math.random() * (HEIGHT/10)); } private Color getRandomColor() { return colors[(int) (Math.random() * colors.length)]; } public static void main(String[] args) { DrawingClient drawing = new DrawingClient(500,600); } }
我使用随机数生成在框架中生成不同类型的Shape。
如果在客户端程序上运行,您会注意到创建第一个Line Object和Oval对象时填充为true和false的延迟。
之后,由于程序使用共享库,因此程序将快速执行。
多次单击"绘制"按钮后,框架如下图所示。
并且您将在命令行中看到以下输出,确认已共享对象。
Creating Line object Creating Oval object with fill=true Creating Oval object with fill=false
多数民众赞成在flyweight模式,我们将在以后的文章中探讨更多的设计模式。
如果喜欢,请在注释部分中分享您的想法,并与他人分享。
JDK中的Flyweight设计模式示例
所有包装器类的valueOf()方法都使用缓存的对象,这些对象显示了Flyweight设计模式的使用。
最好的示例是Java String类String Pool实现。