Java中的Flyweight设计模式

时间:2020-02-23 14:36:13  来源:igfitidea点击:

今天,我们将研究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实现。