Σε αυτό το άρθρο του Java Design Pattern, θα εξετάσουμε το Μοτίβο σχεδίασης Flyweight. Είναι ένα από τα μοτίβα δομικής σχεδίασης. Θα μάθουμε τι είναι αυτό το μοτίβο. Μετά από αυτό, θα εξετάσουμε τα πλεονεκτήματα, τη χρήση και τα μειονεκτήματα του σχεδίου.
Μοτίβο σχεδίασης Flyweight
Το σχέδιο σχεδίασης flyweight εφαρμόζεται όταν χρειάζεται να κατασκευάσουμε μεγάλο αριθμό αντικειμένων που ανήκουν σε μια κλάση. Δεδομένου ότι κάθε αντικείμενο απαιτεί μνήμη, το μοτίβο σχεδίασης flyweight μπορεί να χρησιμοποιηθεί για κοινή χρήση αντικειμένων και μείωση της ζήτησης για μνήμη σε συσκευές χαμηλής μνήμης, όπως κινητές συσκευές ή ενσωματωμένα συστήματα. Στην ουσία, ένα αντικείμενο με μύγα έχει δύο τύπους χαρακτηριστικών: εσωτερικός και εξωτερικόςντο.
Το αντικείμενο flyweight αποθηκεύει και μοιράζεται μια εγγενή ιδιότητα κατάστασης που δεν εξαρτάται από το πλαίσιο του flyweight. Θα πρέπει να κάνουμε τις εγγενείς καταστάσεις αμετάβλητες ως βέλτιστη πρακτική. Οι εξωτερικές καταστάσεις δεν μπορούν να μεταδοθούν, καθώς εξαρτώνται από το πλαίσιο του μύγα. Τα αντικείμενα πελάτη πρέπει να παρέχουν στο αντικείμενο flyweight την εξωτερική κατάσταση κατά τη δημιουργία του αντικειμένου επειδή το διατηρούν.
Πρέπει να διαχωρίσουμε τα χαρακτηριστικά Αντικειμένου σε εσωτερικός και εξωτερικός ιδιότητες για την εφαρμογή του μοτίβου flyweight. Τα εξωτερικά χαρακτηριστικά ορίζονται από τον κώδικα πελάτη και χρησιμοποιούνται για την εκτέλεση συγκεκριμένων εργασιών, ενώ οι εγγενείς ιδιότητες καθιστούν ένα αντικείμενο μοναδικό. Ένας κύκλος αντικειμένων, για παράδειγμα, μπορεί να έχει εξωτερικά χαρακτηριστικά όπως χρώμα και πλάτος.
Πρέπει να δημιουργήσουμε ένα εργοστάσιο Flyweight που θα παράγει κοινόχρηστα αντικείμενα για να εφαρμόσει το μοτίβο flyweight. Ας φανταστούμε, για χάρη του παραδείγματός μας, ότι πρέπει να σχεδιάσουμε κάτι με γραμμές και οβάλ. Ως αποτέλεσμα, θα έχουμε μια διεπαφή Shape και τις πραγματικές υλοποιήσεις της, Line και Oval. Σε αντίθεση με το Line, η οβάλ κατηγορία έχει ένα εγγενές χαρακτηριστικό που καθορίζει εάν θα γεμίσει ή όχι το Oval με ένα συγκεκριμένο χρώμα.
1. Πότε να χρησιμοποιήσετε Flyweight Design Pattern
Το σχέδιο σχεδίασης flyweight χρησιμοποιείται:
- Όταν η εφαρμογή πρέπει να δημιουργήσει μεγάλο αριθμό αντικειμένων.
- Όταν η διαδικασία δημιουργίας ενός αντικειμένου μπορεί να πάρει πολύ χρόνο και απαιτεί πολλή μνήμη.
- Όταν τα εξωτερικά χαρακτηριστικά ενός Αντικειμένου πρέπει να ορίζονται από το λογισμικό πελάτη. Οι ιδιότητες ενός αντικειμένου μπορούν να διαχωριστούν σε εγγενείς και εξωγενείς ιδιότητες.
2. Μοτίβο Flyweight με χρήση Java
Στο προαναφερθέν παράδειγμα, δημιουργούμε μια εφαρμογή Paint Brush όπου ο πελάτης μπορεί να επιλέξει από τρεις διαφορετικούς τύπους πινέλων: ΧΟΝΤΡΟ, ΛΕΠΤΟ ΚΑΙ ΜΕΣΑΙΟ. Μόνο το χρώμα του περιεχομένου θα αλλάξει μεταξύ των σχεδίων του περιεχομένου σε κάθε χοντρό (ή λεπτό ή μεσαίο) πινέλο. Ας δούμε ποια είναι τα βήματα για την εφαρμογή του μοτίβου σχεδίασης flyweight στην Java.
Βήμα 1: Αρχικά, ας δημιουργήσουμε μια διεπαφή Βούρτσα για να σχεδιάσετε τους πίνακες:
public interface Brush {
public void setColor(String color);
public void draw(String content);
}
Βήμα 2: Στη συνέχεια, ας δημιουργήσουμε ENUM
Μέγεθος βούρτσας για μεγέθη βούρτσας.
public enum BrushSize {
THIN,
MEDIUM,
THICK
}
Βήμα 3: Στη συνέχεια, ας δημιουργήσουμε κλάσεις υλοποίησης ThickBrush
, ThinBrush
και MediumBrush
. Θα εφαρμόσουν την Brush
διεπαφή.
public class ThickBrush implements Brush {
/*
intrinsic state - shareable
*/
final BrushSize brushSize = BrushSize.THICK;
/*
extrinsic state - supplied by the client
*/
private String color = null;
public void setColor(String color) {
this.color = color;
}
@Override
public void draw(String content) {
System.out.println("Drawing '" + content + "' in thick color : " + color);
}
}
ThinBrush.java
public class ThinBrush implements Brush {
/*
intrinsic state - shareable
*/
final BrushSize brushSize = BrushSize.THIN;
/*
extrinsic state - supplied by the client
*/
private String color = null;
public void setColor(String color) {
this.color = color;
}
@Override
public void draw(String content) {
System.out.println("Drawing ‘" + content + "' in thin color : " + color);
}
}
MediumBrush.java
public class MediumBrush implements Brush {
/*
intrinsic state - shareable
*/
final BrushSize brushSize = BrushSize.MEDIUM;
/*
extrinsic state - supplied by the client
*/
private String color = null;
public void setColor(String color) {
this.color = color;
}
@Override
public void draw(String content) {
System.out.println("Drawing '" + content + "' in medium color : " + color);
}
}
Εδώ, ο πελάτης θα παρέχει το εξωτερικό χαρακτηριστικό του χρώματος πινέλου. Διαφορετικά, το πινέλο δεν θα έχει διαφορές. Επομένως, στην ουσία, θα παράγουμε μόνο ένα συγκεκριμένο μέγεθος του πινέλου όταν το χρώμα είναι διαφορετικό. Θα το επαναχρησιμοποιήσουμε όταν ένας διαφορετικός πελάτης ή κατάσταση απαιτεί αυτό το μέγεθος και το χρώμα του πινέλου.
Βήμα 4: Στη συνέχεια, ας δημιουργήσουμε το εργοστάσιο BrushFactory
που μπορεί να παρέχει Brush
αντικείμενα.
public class BrushFactory {
private static final HashMap < String, Brush > brushMap = new HashMap < > ();
public static Brush getThickBrush(String color) {
String key = color + "-THICK";
Brush brush = brushMap.get(key);
if (brush != null) {
return brush;
} else {
brush = new ThickBrush();
brush.setColor(color);
brushMap.put(key, brush);
}
return brush;
}
public static Brush getThinBrush(String color) {
String key = color + "-THIN";
Brush brush = brushMap.get(key);
if (brush != null) {
return brush;
} else {
brush = new ThinBrush();
brush.setColor(color);
brushMap.put(key, brush);
}
return brush;
}
public static Brush getMediumBrush(String color) {
String key = color + "-MEDIUM";
Brush brush = brushMap.get(key);
if (brush != null) {
return brush;
} else {
brush = new MediumBrush();
brush.setColor(color);
brushMap.put(key, brush);
}
return brush;
}
Βήμα 5: Στη συνέχεια, ας δημιουργήσουμε το πρόγραμμα πελάτη FlyweightPatternDemo
.
public class FlyweightPatternDemo {
public static void main(String[] args) {
// New thick Red Brush
Brush redThickBrush1 = BrushFactory.getThickBrush("RED");
redThickBrush1.draw("Hello There !!");
// Red Brush is shared
Brush redThickBrush2 = BrushFactory.getThickBrush("RED");
redThickBrush2.draw("Hello There Again !!");
System.out.println("Hashcode: " + redThickBrush1.hashCode());
System.out.println("Hashcode: " + redThickBrush2.hashCode());
// New thin Blue Brush
Brush blueThinBrush1 = BrushFactory.getThinBrush("BLUE"); //created new pen
blueThinBrush1.draw("Hello There !!");
// Blue Brush is shared
Brush blueThinBrush2 = BrushFactory.getThinBrush("BLUE"); //created new pen
blueThinBrush2.draw("Hello There Again!!");
System.out.println("Hashcode: " + blueThinBrush1.hashCode());
System.out.println("Hashcode: " + blueThinBrush2.hashCode());
// New MEDIUM Yellow Brush
Brush yellowThinBrush1 = BrushFactory.getMediumBrush("YELLOW"); //created new pen
yellowThinBrush1.draw("Hello There !!");
// Yellow brush is shared
Brush yellowThinBrush2 = BrushFactory.getMediumBrush("YELLOW"); //created new pen
yellowThinBrush2.draw("Hello There Again!!");
System.out.println("Hashcode: " + yellowThinBrush1.hashCode());
System.out.println("Hashcode: " + yellowThinBrush2.hashCode());
}
}
Εδώ, όπως μπορείτε να δείτε το hashcodes
πινέλων και θα διαπιστώσετε ότι είναι κοινόχρηστα καθώς χρησιμοποιούν το ίδιο χρώμα

3. Διάγραμμα τάξης

3.1. Παραδείγματα πραγματικού κόσμου
Ας ρίξουμε μια ματιά σε μερικά από τα παραδείγματα του πραγματικού κόσμου του Flyweight Design Pattern.
- Σκεφτείτε μια βούρτσα που μπορεί να χρησιμοποιηθεί με ή χωρίς ξαναγέμισμα. Ένα πινέλο μπορεί να χρησιμοποιηθεί για την παραγωγή σχεδίων με Ν αριθμό χρωμάτων, επειδή ένα γέμισμα μπορεί να είναι οποιουδήποτε χρώματος. Εδώ το Brush μπορεί να είναι ένα μύγα με ένα εξωτερικό χαρακτηριστικό, όπως ένα ξαναγέμισμα. Όλα τα άλλα χαρακτηριστικά, όπως το σώμα και ο δείκτης της βούρτσας, μπορεί να είναι εγγενή χαρακτηριστικά που μοιράζονται όλες οι βούρτσες. Ο μόνος άλλος τρόπος για να αναγνωρίσετε ένα πινέλο είναι από το χρώμα του ξαναγεμίσματος του.
- Το ίδιο παράδειγμα κόκκινου πινέλου μπορεί να χρησιμοποιηθεί από οποιαδήποτε λειτουργική μονάδα εφαρμογής που απαιτεί πρόσβαση σε ένα (κοινόχρηστο αντικείμενο). Μόνο όταν απαιτείται βούρτσα διαφορετικού χρώματος, η μονάδα εφαρμογής θα επικοινωνήσει με το εργοστάσιο flyweight για να παραγγείλει άλλη βούρτσα.
- Ένα άλλο παράδειγμα Flyweight είναι μια ομάδα συμβολοσειρών, όπου θα αποθηκεύονται σταθερά αντικείμενα συμβολοσειράς και όταν απαιτείται μια συμβολοσειρά με συγκεκριμένο περιεχόμενο, ο χρόνος εκτέλεσης, εάν είναι δυνατόν, θα επιστρέψει μια αναφορά σε μια ήδη υπάρχουσα σταθερά συμβολοσειράς από το pool.
- Μπορούμε να χρησιμοποιήσουμε μια εικόνα σε μια ιστοσελίδα πολλές φορές σε προγράμματα περιήγησης. Η εικόνα θα φορτωθεί μόνο μία φορά από το πρόγραμμα περιήγησης. Οι επόμενες φορτώσεις θα κάνουν χρήση της κρυφής έκδοσης. Επί του παρόντος, η ίδια εικόνα χρησιμοποιείται πολλές φορές. Λόγω της σταθερής φύσης και της δυνατότητας κοινής χρήσης, η διεύθυνση URL είναι ένα εγγενές χαρακτηριστικό. Οι εξωτερικές ιδιότητες, όπως οι συντεταγμένες, το ύψος και το πλάτος θέσης μιας εικόνας, αλλάζουν ανάλογα με τη θέση (πλαίσιο) στην οποία πρέπει να παρουσιαστούν
3.2. Πλεονεκτήματα μοτίβου flyweight
- Μειώνει τις απαιτήσεις μνήμης μεγάλων, πανομοιότυπα ελεγχόμενων αντικειμένων.
- λιγότερο «γεμάτα αλλά συγκρίσιμα πράγματα» υπάρχουν συνολικά στο σύστημα.
- Παρέχει μια κεντρική μέθοδο για τη διαχείριση των καταστάσεων πολλών «εικονικών» αντικειμένων.
3.3 Προκλήσεις
- Πρέπει να δώσουμε σε αυτήν τη διαμόρφωση flyweight αρκετό χρόνο. Το αρχικό κόστος του χρόνου σχεδιασμού και της τεχνογνωσίας μπορεί να είναι υψηλό.
- Παίρνουμε μια κοινή κλάση προτύπου από τα τρέχοντα αντικείμενα και τη χρησιμοποιούμε για να φτιάξουμε flyweights. Μπορεί να είναι δύσκολο να διορθώσετε και να διατηρήσετε αυτό το πρόσθετο επίπεδο κώδικα.
- Το μοτίβο flyweight συχνά συνδυάζεται με την εργοστασιακή εφαρμογή singleton και απαιτούνται πρόσθετες δαπάνες για την προστασία της μοναδικότητας.
4. Flyweight Pattern Vs Singleton Pattern
Μπορούμε να διατηρήσουμε μόνο ένα στοιχείο στο σύστημα χάρη στο μοτίβο σχεδίασης singleton. Με άλλα λόγια, μόλις παραχθεί το απαραίτητο αντικείμενο, δεν μπορούμε να παράγουμε άλλο. Το υπάρχον αντικείμενο πρέπει να χρησιμοποιείται σε ολόκληρη την εφαρμογή. Όταν είναι απαραίτητο να δημιουργηθεί ένας μεγάλος αριθμός αντικειμένων που είναι παρόμοια αλλά διαφέρουν με βάση ένα εξωτερικό χαρακτηριστικό που παρέχεται από τον πελάτη, το μύγα μοτίβο χρησιμοποιείται.
Περίληψη
Σε αυτή την ανάρτηση, μιλήσαμε για το Μοτίβο σχεδίασης Flyweight. Είδαμε τα παραδείγματα του πραγματικού κόσμου μαζί με ορισμένα πλεονεκτήματα και μειονεκτήματα της χρήσης αυτού του μοτίβου. Είδαμε επίσης μια υλοποίηση Java για αυτό το μοτίβο σχεδίασης. Μπορείτε πάντα να ελέγχετε το δικό μας Αποθετήριο GitHub για τον πιο πρόσφατο πηγαίο κώδικα.
ο Αξία του() Οι μέθοδοι σε όλες τις κατηγορίες περιτυλίγματος χρησιμοποιούν κρυφά αντικείμενα, αποδεικνύοντας την εφαρμογή της αρχής σχεδίασης Flyweight. Η εφαρμογή της κλάσης Java String του String Pool είναι η καλύτερη μελέτη περίπτωσης.