深入理解Java享元设计模式:原理、实现与性能优化应用
这是文章《Java 中的享元设计模式》的第1部分(共1部分)。
内容片段: 今天我们将研究享元设计模式。
享元设计模式
根据《设计模式》所述,享元设计模式的意图是:
使用共享来高效地支持大量的细粒度对象。
享元设计模式是一种结构设计模式,就像门面模式、适配器模式和装饰器模式一样。享元设计模式用于在需要创建大量类的对象时使用。由于每个对象消耗的内存空间对于低内存设备(如移动设备或嵌入式系统)可能很关键,因此可以应用享元设计模式来通过共享对象减少内存负载。在应用享元设计模式之前,我们需要考虑以下因素:
- 应用程序需要创建的对象数量应该非常庞大。
- 对象创建对内存消耗较大,同时也可能耗时。
- 对象属性可以分为内在属性和外在属性,对象的外在属性应该由客户端程序定义。
应用享元模式时,我们需要将对象属性分为内在属性和外在属性。内在属性使得对象独一无二,而外在属性由客户端代码设置,并用于执行不同的操作。例如,一个圆形对象可以具有外在属性,如颜色和宽度。为了应用享元模式,我们需要创建一个享元工厂,它返回共享对象。以我们的例子来说,我们需要创建一个包含线条和椭圆的绘图。因此,我们将拥有一个Shape接口以及它的具体实现Line和Oval。Oval类将具有内在属性,用于确定是否使用给定的颜色来填充椭圆,而Line类将不具有任何内在属性。
享元设计模式接口和具体类
形状.java
package com.Olivia.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);
}
线.java
package com.Olivia.design.flyweight;
import java.awt.Color;
import java.awt.Graphics;
public class Line implements Shape {
public Line(){
System.out.println("创建线条对象");
//添加时间延迟
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.Olivia.design.flyweight;
import java.awt.Color;
import java.awt.Graphics;
public class Oval implements Shape {
//内在属性
private boolean fill;
public Oval(boolean f){
this.fill=f;
System.out.println("创建椭圆对象,填充属性为="+f);
//添加时间延迟
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);
}
}
}
请注意,我故意在创建具体类的对象时引入了延迟,以此来说明享元模式可用于实例化时间较长的对象。
享元工厂
客户端程序将使用享元工厂来实例化对象,因此我们需要在工厂中保持一个对象的映射,该映射不应该被客户端应用程序访问。每当客户端程序调用获取对象实例的方法时,应该从HashMap中返回对象,如果找不到则创建一个新的对象并放入映射中,然后返回。我们需要确保在创建对象时考虑所有内在属性。我们的享元工厂类的代码如下:ShapeFactory.java
package com.Olivia.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枚举类型来确保类型安全,使用Java组合(shapes map)和工厂模式。
享元设计模式客户端示例
以下是一个使用享元模式实现的示例程序,DrawingClient.java。
package com.Olivia.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.Olivia.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("绘制");
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);
}
}
我使用随机数生成来生成不同类型的形状在我们的框架上。如果你运行上面的客户端程序,你会注意到创建第一个线对象和填充为true和false的椭圆对象的延迟。之后程序执行非常快,因为它使用了共享的对象。点击”绘制”按钮多次后,框架的样子如下图所示。你会在命令行中看到以下输出,确认对象是共享的。
创建线条对象
创建椭圆对象,填充属性为=true
创建椭圆对象,填充属性为=false
关于享元模式的讲解就到这里了,我们将在未来的帖子中继续介绍更多的设计模式。如果你喜欢的话,请在评论区分享你的想法,并与他人分享。
Java开发环境中的享元设计模式示例
所有的包装类的valueOf()方法都使用了缓存对象,展现了享元设计模式的使用。最好的例子是Java字符串类的字符串池实现。
享元设计模式的重要要点
- 在我们的例子中,客户端代码不被强制使用享元工厂来创建对象,但我们可以强制它以确保客户端代码使用享元模式的实现,不过这是一种特定应用的完整设计决策。
- 享元模式引入了复杂性,如果共享对象的数量很大,那么内存和时间之间存在着一个权衡,所以我们需要根据需求来谨慎使用它。
- 当对象的内在属性数量巨大时,实现工厂类会变得复杂,因此享元模式的实现是不适用的。
这就是关于Java中享元设计模式的全部内容。