今天我們將探討享元設計模式。
享元設計模式
根據 GoF,享元設計模式的目的是:
使用共享來有效支援大量的細粒度物件
享元設計模式是一種結構型設計模式,就像外觀模式、適配器模式和裝飾器模式。當我們需要創建大量的類別物件時,可以使用享元設計模式。由於每個物件都佔用記憶體空間,這對於低記憶體設備(例如移動設備或嵌入式系統)來說可能很關鍵,因此可以應用享元設計模式來共享物件以減輕記憶體負擔。在應用享元設計模式之前,我們需要考慮以下因素:
- 應用程式需要創建的物件數量龐大。
- 物件的創建對記憶體要求高且可能耗時。
- 物件的屬性可以分為內在和外在屬性,物件的外在屬性應由客戶端程式定義。
為了應用享元模式,我們需要將物件的屬性分為內在和外在屬性。內在屬性使物件獨特,而外在屬性則由客戶端程式設定並用於執行不同的操作。例如,一個圓形物件可以有外在屬性,如顏色和寬度。為了應用享元模式,我們需要創建一個享元工廠,返回共享的物件。在我們的例子中,假設我們需要創建一個包含線和橢圓的繪圖。所以我們將有一個介面Shape
,以及它的具體實現Line
和Oval
。橢圓類將具有內在屬性來確定是否填充橢圓以及填充的顏色,而線將不具有任何內在屬性。
享元設計模式介面和具體類
Shape.java
package com.journaldev.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.journaldev.design.flyweight;
import java.awt.Color;
import java.awt.Graphics;
public class Line implements Shape {
public Line(){
System.out.println("Creating Line object");
//添加延遲時間
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
package com.journaldev.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("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);
}
}
}
請注意,我故意在創建具體類的對象時引入了延遲,以證明享元模式可用於實例化需要很長時間的對象。
享元工廠
客戶端程序將使用享元工廠來實例化對象,因此我們需要在工廠中保留一個對象映射,該映射對客戶端應用程序不可見。每當客戶端程序調用獲取對象的方法時,應從HashMap返回該對象,如果找不到則創建一個新對象並將其放入映射中,然後返回它。我們需要確保在創建對象時考慮所有內在屬性。我們的享元工廠類如下代碼所示。ShapeFactory.java
package com.journaldev.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映射)和工廠模式。
享元设计模式客户端示例
以下是一个使用享元模式实现的示例程序。DrawingClient.java
package com.journaldev.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.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);
}
}
I have used random number generation to generate different type of Shapes in our frame. If you run above client program, you will notice the delay in creating first Line Object and Oval objects with fill as true and false. After that the program executes quickly since its using the shared objects. After clicking “Draw” button multiple times, the frame looks like below image. And you will see following output in command line confirming that Objects are shared.
Creating Line object
Creating Oval object with fill=true
Creating Oval object with fill=false
享元模式就介绍到这里,我们将在以后的文章中了解更多设计模式。如果你喜欢,欢迎在评论区分享你的想法,并与他人分享。
JDK中的享元设计模式示例
所有的包装类valueOf()
方法都使用了缓存对象,展示了享元设计模式的使用。最好的例子就是Java String类的字符串池实现。
享元设计模式重要点
- 在我们的例子中,客户端代码不强制使用享元工厂来创建对象,但我们可以强制使用享元模式的实现,但这是根据特定应用的完全设计决策。
- 享元模式引入了复杂性,如果共享对象的数量很大,那么在内存和时间之间存在一种权衡,因此我们需要根据我们的需求来明智地使用它。
- 当对象的内在属性数量很大时,享元模式的实现就不太有用了,这会使工厂类的实现变得复杂。
以上就是关于Java中享元设计模式的全部内容。
Source:
https://www.digitalocean.com/community/tutorials/flyweight-design-pattern-java