היום נבחן את דפוס העיצוב Flyweight.
דפוס עיצוב Flyweight
לפי GoF, דפוס עיצוב Flyweight מתכוון למשהו כזה:
השימוש בשיתוף כדי לתמוך בכמויות גדולות של אובייקטים מעודפים מעולם
דפוס עיצוב Flyweight הוא דפוס עיצוב תשתיתי כמו דפוס פסד, דפוס מתאים ו־דפוס עיטוף. דפוס עיצוב Flyweight נעשה שימוש כאשר יש צורך ליצור המון אובייקטים ממחלקה מסוימת. מאחר שכל אובייקט משתמש במקום בזיכרון שיכול להיות קריטי להתקנים עם זיכרון נמוך, כמו מכשירים ניידים או מערכות מוטבעות, ניתן ליישם את דפוס העיצוב Flyweight כדי להפחית את העומס על הזיכרון על ידי שיתוף אובייקטים. לפני שאנו מחילים את דפוס העיצוב Flyweight, יש להתחשב בשלבים הבאים:
- מספר האובייקטים שיש ליצור על ידי היישום צריך להיות עצום.
- יצירת האובייקט דורשת הרבה משאבי זיכרון ויכולה להיות זמן רב.
- המאפיינים של האובייקט ניתן לחלק לתכונות פנימיות ותכונות חיצוניות, תכונות חיצוניות של אובייקט צריכות להיגדר על ידי תוכנת הלקוח.
כדי ליישם את דפוס ה־flyweight, עלינו לחלק את מאפייני האובייקט לתכונות פנימיות ו־חיצוניות. תכונות פנימיות הופכות את האובייקט לייחודי בעוד שתכונות חיצוניות מוגדרות על ידי קוד הלקוח ומשמשות לביצוע פעולות שונות. לדוגמה, אובייקט מעגל יכול לכלול תכונות חיצוניות כמו צבע ורוחב. כדי ליישם את דפוס ה־flyweight, עלינו ליצור מפעל Flyweight שמחזיר את האובייקטים המשותפים. לדוגמה שלנו, נניח שאנו צריכים ליצור שרבורת עם קווים ואליפסות. לכן נגדיר ממשק Shape
ואת המימושים הממותגים שלו כמו Line
ו־Oval
. המחלקה Oval תכיל תכונה פנימית שתקבע האם למלא את האליפסה בצבע נתון או לא בעוד ש־Line לא תכיל תכונה פנימית.
ממשק דפוס העיצוב Flyweight ומחלקות מוחלטות
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);
}
}
}
שים לב שבכוונה הכנתי השהייה ביצירת אובייקט של מחלקות קונקרטיות כדי להדגיש כי דפוס ה- flyweight יכול להיות בשימוש עבור אובייקטים שדורשים המון זמן בעת הפעלתם.
מפעל ה- Flyweight
מפעל ה- flyweight ישמש את תוכניות הלקוח כדי ליצור את האובייקט, לכן עלינו לשמור על מפתח של אובייקטים במפעל שלא יהיו נגישים על ידי יישום הלקוח. בכל פעם שתוכנית הלקוח קוראת לפעולה לקבלת מופע של אובייקט, עליו להתקבל מהמפתח, אם לא נמצא אז ליצור אובייקט חדש ולהכניס אותו למפתח ולאחר מכן להחזיר אותו. עלינו לוודא שכל התכונות הפנימיות נלקחות בחשבון בעת יצירת האובייקט. מחלקת המפעל שלנו נראית כמו הקוד למטה.
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.
דוגמה ללקוח של תבנית עיצוב המשקל הטסה
למטה תוכנית דוגמה שצריכה את המימוש של תבנית העיצוב המשקל הטסה. 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()
משתמשות באובייקטים מטמונים המציגים שימוש בתבנית עיצוב המשקל הטסה. הדוגמה הטובה ביותר היא מימוש המחלקה String Pool של המחרוזת ב-Java.
נקודות חשובות בתכנון התבנית של Flyweight
- בדוגמה שלנו, לקוד הלקוח אין התחייבות ליצירת אובייקט באמצעות מפענח ה־Flyweight, אך אפשר לכן לאלץ זאת כדי לוודא שקוד הלקוח משתמש במימוש של תבנית Flyweight. אך זו החלטת עיצוב מלאה עבור אפליקציה מסוימת.
- השימוש בתבנית Flyweight מכניס פורקנות, ואם מספר האובייקטים המשותפים גדול, יש מחלוקת בין זמן לזיכרון, ולכן יש צורך להשתמש בה בצורה שכוללת מדוד על פי דרישותינו.
- מימוש של תבנית Flyweight אינו שימושי כאשר מספר המאפיינים הפנימיים של אובייקט הוא גדול, וכלולה המימוש של מחלקת היצירה מסובכת.
זהו הכל בנוגע לתבנית העיצוב של Flyweight בשפת Java.
Source:
https://www.digitalocean.com/community/tutorials/flyweight-design-pattern-java