提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
- 1. 享元设计模式介绍
- 1.1 定义
- 1.2 原理
- 1.3 使用场景
- 1.4 注意事项
- 2. 享元设计模式角色
- 2.1 抽象享元角色(AbstractFlyWeight)
- 2.2 具体享元角色(ConcreteFlyWeight)
- 2.3 享元工厂角色(FlyWeightFactory)
- 2.4 客户端角色(Client)
- 3. 享元设计模式优点
- 3.1 减少内存使用
- 3.2 提升性能
- 3.3 简化操作
- 4. 享元设计模式示例
- 4.1 编写抽象享元角色(Shape.java)
- 4.2 编写具体享元角色(Line.java和Oval.java)
- 4.3 编写享元工厂角色(ShapeFactory.java)
- 4.4 编写客户端示例代码
- 2. 总结
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
及其具体实现Line
和Oval
。Oval
类将具有内在属性来确定是否用给定的颜色填充椭圆,而 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 delaytry {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}}@Overridepublic 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 propertyprivate 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();}}@Overridepublic 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. 总结
享元模式适用于存在大量相似对象的场景,特别是当对象的内部状态较少并且可以共享时。这种模式通过内部状态和外部状态的分离,使得外部状态的改变可以在外部进行设置,尽管这可能会带来一定的复杂性,但总体而言,享元模式在处理大量相似对象时表现出色。
以上就是关于享元模式的全部内容,我们将在以后的文章中探讨更多设计模式。如果您喜欢它,请在评论部分分享您的想法并与其他人分享。