今日は、Flyweightデザインパターンについて調査します。
Flyweightデザインパターン
GoFによれば、flyweightデザインパターンの意図は次の通りです:
共有を利用して、多くの細かいオブジェクトを効率的にサポートします
Flyweightデザインパターンは構造デザインパターンであり、Facadeパターン、Adapterパターン、およびDecoratorパターンのように。 Flyweightデザインパターンは、クラスの多くのオブジェクトを作成する必要がある場合に使用されます。すべてのオブジェクトがメモリスペースを消費するため、それが低メモリデバイス(モバイルデバイスや組み込みシステムなど)にとって重要である場合、メモリへの負荷を減らすためにFlyweightデザインパターンを適用できます。 Flyweightデザインパターンを適用する前に、以下の要因を考慮する必要があります:
- アプリケーションによって作成されるオブジェクトの数は膨大であるべきです。
- オブジェクトの作成がメモリに負担がかかり、時間がかかる可能性があります。
- オブジェクトのプロパティは、固有のものと外部のものに分けることができます。オブジェクトの外部のプロパティは、クライアントプログラムによって定義されるべきです。
フライウェイトパターンを適用するためには、オブジェクトのプロパティを固有のものと外部のものに分ける必要があります。固有のプロパティはオブジェクトを一意にしますが、外部のプロパティはクライアントコードによって設定され、異なる操作を行うために使用されます。たとえば、オブジェクトの円には色や幅などの外部のプロパティがあります。フライウェイトパターンを適用するには、共有オブジェクトを返すフライウェイトファクトリを作成する必要があります。例として、線と楕円を含む図形を作成する必要があるとします。そのため、Shape
とその具象実装であるLine
とOval
のインターフェースがあります。Ovalクラスには、指定された色で楕円を塗りつぶすかどうかを決定する固有のプロパティがありますが、Lineには固有のプロパティはありません。
フライウェイトデザインパターンのインターフェースと具象クラス
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);
}
}
}
わかりました。以下は日本語への翻訳です:
注意してください。わたしは具象クラスのオブジェクトを作成する際に意図的に遅延を導入しました。これは、フライウェイトパターンが多くの時間を必要とするオブジェクトに使用できることを示すためです。
フライウェイトファクトリ
クライアントプログラムがオブジェクトをインスタンス化するためにフライウェイトファクトリが使用されるため、クライアントアプリケーションからアクセスできないオブジェクトのマップをファクトリに保持する必要があります。クライアントプログラムがオブジェクトのインスタンスを取得するために呼び出しを行うたびに、そのオブジェクトはハッシュマップから返されるべきです。見つからない場合は、新しいオブジェクトを作成してマップに追加し、それを返します。オブジェクトを作成する際には、すべての固有のプロパティが考慮されていることを確認する必要があります。私たちのフライウェイトファクトリクラスは以下のコードのようになります。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;
}
}
Java Enumの使用、Java Composition(シェイプマップ)、およびgetShape
メソッド内のFactoryパターンに注意してください。
Flyweight Design Pattern Client Example
以下は、Flyweightパターンの実装を消費するサンプルプログラムです。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
これでFlyweightパターンについては以上です。今後の投稿でさらにデザインパターンを見ていきます。お気に入りの場合は、コメントセクションでご意見を共有して他の方とも共有してください。
JDKでのFlyweightデザインパターンの例
すべてのwrapper classes valueOf()
メソッドは、Flyweightデザインパターンの使用を示すキャッシュされたオブジェクトを使用しています。最良の例はJava StringクラスのString Pool実装です。
フライウェイトデザインパターンの重要なポイント
- 私たちの例では、クライアントコードはFlyweightファクトリを使用してオブジェクトを作成することを強制されていませんが、特定のアプリケーションに完全な設計上の決定がある場合、クライアントコードがフライウェイトパターンの実装を使用するように強制することができます。
- フライウェイトパターンは複雑さを導入し、共有オブジェクトの数が多い場合、メモリと時間の間でトレードオフが発生しますので、私たちの要件に基づいて賢明に使用する必要があります。
- フライウェイトパターンの実装は、オブジェクトの固有のプロパティの数が多い場合、Factoryクラスの実装が複雑になるため、役立ちません。
これで、Javaにおけるフライウェイトデザインパターンに関する説明は以上です。
Source:
https://www.digitalocean.com/community/tutorials/flyweight-design-pattern-java