Java结构型设计模式-享元设计模式

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


在这里插入图片描述

1. 享元设计模式介绍

1.1 定义

享元模式(英语:Flyweight Pattern)是一种软件设计模式,属结构型设计模式。类似于外观模式适配器模式装饰模式

1.2 原理

享元模式通过共享已经存在的对象来减少对象的创建数量,‌从而节省系统资源,‌提高系统性能。‌这种模式特别适用于大量相似对象的场景,‌通过内部状态和外部状态的区分,‌实现了对象的复用和高效管理。‌

1.3 使用场景

当我们需要创建大量类的对象时,可以使用享元设计模式。由于每个对象都会占用内存空间,而这对于低内存设备(如移动设备或嵌入式系统)来说至关重要,因此可以应用享元设计模式,通过共享对象来减少内存负载

要应用享元模式,我们需要将对象属性分为内在属性外在属性内在属性使对象独一无二,而外在属性由客户端代码设置并用于执行不同的操作。例如,对象 Circle 可以具有外在属性,例如颜色和宽度。要应用享元模式,我们需要创建一个返回共享对象的享元工厂。

1.4 注意事项

在应用享元设计模式之前,我们需要考虑以下因素:

  • 应用程序要创建的对象数量应该非常庞大
  • 对象的创建会占用大量内存并且也很耗时。
  • 对象属性可以分为内在属性外在属性,对象的外在属性应该由客户端程序定义。

2. 享元设计模式角色

在这里插入图片描述

享元设计模式包含四个主要角色:‌

2.1 抽象享元角色(‌AbstractFlyWeight)‌

这个角色为具体享元角色规定了必须实现的方法,‌在Java中可以是抽象类或接口。‌它定义了享元对象的接口,‌但不涉及具体的实现细节。‌

2.2 具体享元角色(‌ConcreteFlyWeight)‌

这个角色实现了抽象享元角色定义的方法。‌如果存在内蕴状态,‌具体享元角色还负责为内蕴状态提供存储空间。‌

2.3 享元工厂角色(‌FlyWeightFactory)‌

这个角色负责创建和管理享元对象。‌它提供一个用于存储享元对象的享元池。‌当用户需要对象时,‌享元工厂首先尝试从享元池中获取对象。‌如果享元池中不存在所需对象,‌则创建一个新的享元对象并返回给用户,‌同时在享元池中保存该新增对象。‌

2.4 客户端角色(‌Client)‌

这个角色维护对所有享元对象的引用,‌并存储对应的外蕴状态。‌它使用享元工厂来生成抽象享元角色,‌是享元模式的最终使用者。‌

3. 享元设计模式优点

3.1 减少内存使用

享元模式通过共享对象来减少系统中的对象数量,‌从而节省内存空间。‌这是因为通过共享相同或相似的对象,‌避免了不必要的对象创建,‌减少了内存的消耗12。‌

3.2 提升性能

由于减少了对象的数量,‌系统性能得到了提高。‌这是因为共享对象可以避免重复创建对象的开销,‌使得系统运行更加高效12。‌

3.3 简化操作

对于具有相同或相似内部状态的对象,‌可以共享一个享元对象,‌简化了对对象的操作。‌这有助于减少代码复杂度,‌使得维护和扩展更加容易。‌

在这里插入图片描述

4. 享元设计模式示例

在下面的例子中,假设我们需要创建一个带有线条和椭圆的图形。因此,我们将有一个接口Shape及其具体实现LineOvalOval 类将具有内在属性来确定是否用给定的颜色填充椭圆,而 Line 则没有任何内在属性。

4.1 编写抽象享元角色(Shape.java)‌

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);
}

4.2 编写具体享元角色(Line.java和Oval.java)

Line.java

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);
	}
}

Oval.java
请注意,我特意引入了创建具体类的对象时的延迟,以表明享元模式可以用于实例化时需要大量时间的对象。

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);
		
		// 添加一个延迟
		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);
		}
	}
}

4.3 编写享元工厂角色(ShapeFactory.java)

客户端程序将使用 flyweight 工厂来实例化对象,因此我们需要在工厂中保留一个对象映射,客户端应用程序不应访问该映射。每当客户端程序调用以获取对象实例时,它都应从 HashMap 返回,如果没有找到,则创建一个新对象并将其放入 Map 中,然后返回它。我们需要确保在创建对象时考虑所有内在属性。我们的 flyweight 工厂类如下所示。
ShapeFactory.java

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 hash缓存中 ,方便下次快速获取,提高性能
			shapes.put(type, shapeImpl);
		}
		return shapeImpl;
	}
	
	public static enum ShapeType{
		OVAL_FILL,OVAL_NOFILL,LINE;
	}
}

4.4 编写客户端示例代码

下面是一个使用享元模式实现的示例程序。
DrawingClient.java

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.journaldev.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);
	}
}

我使用随机数生成在我们的框架中生成不同类型的形状。如果您运行上述客户端程序,您会注意到在创建第一个线对象和椭圆对象时出现延迟,其中填充为真和假。之后,由于使用共享对象,程序执行速度很快。多次单击“绘制”按钮后,框架如下图所示。

在这里插入图片描述
您将在命令行中看到以下输出,确认对象是共享的。

Creating Line object
Creating Oval object with fill=true
Creating Oval object with fill=false

2. 总结

在这里插入图片描述
享元模式适用于存在大量相似对象的场景,‌特别是当对象的内部状态较少并且可以共享时。‌这种模式通过内部状态和外部状态的分离,‌使得外部状态的改变可以在外部进行设置,‌尽管这可能会带来一定的复杂性,‌但总体而言,‌享元模式在处理大量相似对象时表现出色。‌

以上就是关于享元模式的全部内容,我们将在以后的文章中探讨更多设计模式。如果您喜欢它,请在评论部分分享您的想法并与其他人分享。

  • 14
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

龙殿殿主

你的打赏是我精心创作的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值