设计模式之享元模式 今天,我们将研究Flyweight设计模式。
什么是享元设计模式 享元模式(Flyweight Pattern)主要用于减少创建对象的数量,以减少内存占用和提高性能。这种类型的设计模式属于结构型模式,例如Facade
模式,Adapter
模式和Decorator
模式。它提供了减少对象数量从而改善应用所需的对象结构的方式。并且使用共享有效地支持大量细粒度的对象。
当我们需要创建一个类的许多对象时,可以使用Flyweight设计模式。 由于每个对象都会占用对于低内存设备(例如移动设备或嵌入式系统)至关重要的内存空间,因此可以应用轻量级设计模式来通过共享对象来减少内存负载。
享元设计模式使用场景 在应用flyweight设计模式之前,我们需要考虑以下因素:
应用程序要创建的对象数量应该很大。
对象的创建占用大量内存,并且也很耗时。
对象属性可以分为内部属性和外部属性,对象的外部属性应由客户端程序定义。
要应用flyweight模式,我们需要将Object属性分为内部属性和外部属性。 内部属性使对象唯一,而外部属性由客户端代码设置并用于执行不同的操作。 例如,Circle
对象可以具有外部属性,例如颜色和宽度。
为了应用flyweight模式,我们需要创建一个Flyweight factory
工厂来返回共享对象。 对于我们的示例,假设我们需要使用线条和椭圆形创建图形。 因此,我们将有一个Shape
界面及其具体实现,如Line
和Oval
。 椭圆类将具有固有属性,以确定是否用给定的颜色填充椭圆,而Line
将不具有任何固有属性。
Flyweight设计模式代码示例 Shape.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import java.awt.Graphics;import java.awt.Color;public interface Shape { void draw (Graphics g, int x, int y, int width, int height, Color color) ; }
Line.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 import java.awt.Graphics;import java.awt.Color;import java.util.concurrent.TimeUnit;public class Line implements Shape { public Line () { System.out.println("Creating Line object." ); try { TimeUnit.SECONDS.sleep(2 ); } catch (InterruptedException e) { e.printStackTrace(); } } @Override public void draw (Graphics line, int x, int y, int width, int height, Color color) { line.setColor(color); line.drawLine(x, y, width, height); } }
Oval.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 import java.awt.Graphics;import java.awt.Color;import java.util.concurrent.TimeUnit;public class Oval implements Shape { private boolean fill; public Oval (boolean fill) { this .fill = fill; System.out.println("Creating Oval object with fill = " + fill); try { TimeUnit.SECONDS.sleep(2 ); } 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 Factory flyweight工厂将由客户端程序用来实例化Object,因此我们需要在工厂中保留一个Object映射,客户端应用程序不应访问该Map。
每当客户端程序调用以获取Object的实例时,都应从HashMap
返回它,如果找不到,则创建一个新的Object并放入Map中,然后返回它。 我们需要确保在创建对象时考虑所有固有属性。
我们的flyweight工厂类看起来像下面的代码。
ShapeFactory.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 import java.util.HashMap;import java.util.Map;public class ShapeFactory { public static final Map<ShapeType, Shape> SHAPES = new HashMap <>(); public static Shape getShape (ShapeType type) { Shape shape = SHAPES.get(type); if (shape == null ) { if (type.equals(ShapeType.OVAL_FILL)) { shape = new Oval (true ); } else if (type.equals(ShapeType.OVAL_NOFILL)) { shape = new Oval (false ); } else if (type.equals(ShapeType.LINE)) { shape = new Line (); } SHAPES.put(type, shape); } return shape; } }
请注意,在getShape()
方法中将Java Enum
用于类型安全,Java Composition
(形状映射)和Factory
模式。
ShapeType.java
1 2 3 4 5 public enum ShapeType { OVAL_FILL, OVAL_NOFILL, LINE; }
Flyweight设计模式客户端示例 下面是使用flyweight模式实现的示例程序。
DrawingClient.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 import javax.swing.*;import java.awt.*;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) throws HeadlessException { 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(e -> { 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 drawingClient = new DrawingClient (500 , 600 ); } }
我已经使用随机数生成在框架中生成不同类型的Shape。
如果在客户端程序上运行,您会注意到创建第一个Line和Oval对象时填充为true和false的延迟。 之后,由于程序使用共享库,因此程序将快速执行。
多次单击“绘制”按钮后,框架如下图所示。
并且您将在命令行中看到以下输出,确认已共享对象。
1 2 3 Creating Oval object with fill = false Creating Line object .Creating Oval object with fill = true
JDK中的Flyweight设计模式示例 所有包装器类的valueOf()
方法都使用缓存的对象,这些对象显示了Flyweight设计模式的使用。 最好的示例是Java String
类String Pool
实现。
Flyweight设计模式要点
在我们的示例中,没有强制客户端代码使用Flyweight工厂创建对象,但是我们可以强制这样做以确保客户端代码使用Flyweight模式实现,但这是针对特定应用程序的完整设计决策。
Flyweight模式引入了复杂性,如果共享对象的数量巨大,那么内存和时间之间就需要进行权衡,因此我们需要根据我们的要求明智地使用它。
当Object的固有属性数量巨大时,Flyweight模式实现就没有用,这使得Factory类的实现变得复杂。
这就是Java中的Flyweight设计模式。