-
FlyweightModeling/DesignPattern 2020. 2. 28. 23:13
1. Overview
Our system needs a large number of objects of a particular class and maintaining these instances is a performance concern. Flyweight allows us to share an object in multiple contexts. But instead of sharing the entire object, which may not be feasible, we divide the object state into two parts. intrinsic (the state that is shared in every context) and extrinsic state (context-specific state). We create objects with only intrinsic state and share them in multiple contexts.
The client or user of an object provides the extrinsic state to object to carrying out its functionality We provide a factory so the client can get required flyweight objects based on some key to identifying flyweight.
2. Description
2.1 Implementation
- We start by identifying intrinsic and extrinsic state of our object
- We create an interface for flyweight to provide common methods that accept extrinsic state
- In the implementation of shared flyweight, we add intrinsic state and also implement methods
- In unshared flyweight implementation, we simply ignore the extrinsic state argument as we have all state within object.
- Next, we implement the flyweight factory which caches flyweight and also provides method to get them
- In our client, we either maintain the extrinsic state or compute it on the fly when using flyweight
2.2 Consideration
2.2.1 Implementation
- A factory is necessary with flyweight design pattern as the client code needs easy way to get hold of a shared flyweight. Also, the number of shared instances can be large so a central place is a good strategy to keep track of all of them.
- Flyweight's intrinsic state should be immutable for the successful use of flyweight pattern.
2.2.2 Design
- Usability of flyweight is entirely dependent upon presence of the sensible extrinsic state in object which can be moved out of object without any issue
- Some other design patterns like state and strategy can make the best use of flyweight pattern
2.3 Pitfalls
- Runtime cost may be added for maintaining extrinsic state. Client code has to either maintain it or compute it every time it needs to use flyweight
- It is often difficult to find perfect candidate objects for flyweight. Graphical applications benefit heavily from this pattern however a typical web application may not have a lot of use for this pattern.
3. Usage
- java.lang.Integer, Short, Byte, and etc. Here the value of static method servers as the factory method
- String pool which is maintained by JVM. intern() on a string object to explicitly request this String object to be interned.
4. Comparison with Object Pool
Flyweight Object Pool State of flyweight object is divided. Client must provide part of state to it A pooled object contains all of its states encapsulated within itself In a typical usage client will not change intrinsic state of flyweight instance as it is shared Client can and will change the state of pooled objects 5. Example
//Interface implemented by Flyweights interface ErrorMessage { //Get error message String getText(String code); } //A concrete Flyweight. Instance is shared class SystemErrorMessage implements ErrorMessage { // Some error message $errorCode private String messageTemplate; //http://somedomain.com/help?error= private String helpUrlBase; public SystemErrorMessage(String messageTemplate, String helpUrlBase) { this.messageTemplate = messageTemplate; this.helpUrlBase = helpUrlBase; } @Override public String getText(String code) { return messageTemplate.replace("$errorCode", code) + helpUrlBase+code; } } //Unshared concrete flyweight. class UserBannedErrorMessage implements ErrorMessage { //All state is defined here private String caseId; private String remarks; private Duration banDuration; private String msg; public UserBannedErrorMessage(String caseId) { //Load case info from DB. this.caseId = caseId; remarks = "You violated terms of use."; banDuration = Duration.ofDays(2); msg = "You are BANNED. Sorry. \nMore information:\n"; msg += caseId +"\n"; msg += remarks+"\n"; msg += "Banned For:" +banDuration.toHours() + " Hours"; } //We ignore the extrinsic state argument @Override public String getText(String code) { return msg; } public String getCaseNo() { return caseId; } } //Flyweight factory. Returns shared flyweight based on key class ErrorMessageFactory { //This serves as key for getting flyweight instance public enum ErrorType {GenericSystemError, PageNotFoundError, ServerError} private static final ErrorMessageFactory FACTORY = new ErrorMessageFactory(); public static ErrorMessageFactory getInstance() { return FACTORY; } private Map<ErrorType, SystemErrorMessage> errorMessages = new HashMap<>(); private ErrorMessageFactory() { errorMessages.put(ErrorType.GenericSystemError, new SystemErrorMessage("A genetic error of type $errorCode occured. Please refer to:\n", "http://google.com/q=")); errorMessages.put(ErrorType.PageNotFoundError, new SystemErrorMessage("Page not foun. An error of type $errorCode occured. Please refer to:\n", "http://google.com/q=")); } public SystemErrorMessage getError(ErrorType type) { return errorMessages.get(type); } public UserBannedErrorMessage getUserBannedMessage(String caseId) { return new UserBannedErrorMessage(caseId); } } public class Client { public static void main(String[] args) { SystemErrorMessage msg1 = ErrorMessageFactory.getInstance().getError(ErrorMessageFactory.ErrorType.GenericSystemError); System.out.println(msg1.getText("4056")); UserBannedErrorMessage msg2 = ErrorMessageFactory.getInstance().getUserBannedMessage("1202"); System.out.println(msg2.getText(null)); } }
6. Reference
'Modeling > DesignPattern' 카테고리의 다른 글
Iterator (0) 2020.02.29 Command (0) 2020.02.29 Dependency Inversion Principle (DIP) (0) 2020.02.28 Interface Segregation Principle (ISP) (0) 2020.02.28 Liskov Substitution Principle (LSP) (0) 2020.02.28