Factory and Flyweight Design Patterns
Download
Report
Transcript Factory and Flyweight Design Patterns
CSE 403
Design Patterns:
Singleton, Memento, Flyweight, Factory, Command
Reading:
Simply Singleton and Make Your Apps Fly (R. Geary)
These lecture slides are copyright (C) Marty Stepp, 2007. They may not be rehosted, sold, or
modified without expressed permission from the author. All rights reserved.
1
Pattern: Singleton
a class that has only one instance
2
Restricting object creation
Problem: Sometimes we will really only ever need one
instance of a particular class.
examples: keyboard reader, bank data collection
We'd like to make it illegal to have more than one.
Why we care:
Creating lots of objects can take a lot of time.
Extra objects take up memory.
It is a pain to deal with different objects floating around if they
are essentially the same.
3
Singleton pattern
singleton: an object that is the only object of its type
ensures that a class has at most one instance
provides a global access point to that instance
takes responsibility of managing that instance away from the
programmer (illegal to construct more instances)
provide accessor method that allows users to see the (one and
only) instance
possibly the most known / popular design pattern! (this should
tell you something)
4
Restricting objects, continued
One way to avoid creating objects:
use static methods instead
Math, System, JOptionPane
is this a good alternative choice? Why or why not?
Problem: lacks flexibility
Example: static methods can't be passed as an argument to a
method, nor returned
Problem: cannot be extended
Example: static methods can't be subclassed and overridden
like a singleton's could be
5
Implementing Singleton
make constructor(s) private so that they can not be
called from outside
declare a single static private instance of the class
write a public getInstance() or similar method that
allows access to the single instance
possibly protect / synchronize this method to ensure that it will
work in a multi-threaded program
6
Singleton sequence diagram
7
Singleton example
consider a singleton class RandomGenerator that
generates random numbers
public class RandomGenerator {
private static RandomGenerator gen = new RandomGenerator();
public static RandomGenerator getInstance() {
return gen;
}
private RandomGenerator() {}
...
}
possible problem: always creates the instance, even if
it isn't used
8
Singleton example 2
variation: don't create the instance until needed
// Generates random numbers.
public class RandomGenerator {
private static RandomGenerator gen = null;
public static RandomGenerator getInstance() {
if (gen == null) {
gen = new RandomGenerator();
}
return gen;
}
...
}
What could go wrong with this version?
9
Singleton example 3
variation: solve concurrency issue by locking
// Generates random numbers.
public class RandomGenerator {
private static RandomGenerator gen = null;
public static synchronized RandomGenerator getInstance() {
if (gen == null) {
gen = new RandomGenerator();
}
return gen;
}
...
}
Is anything wrong with this version?
10
Singleton example 4
variation: solve concurrency issue without unnecessary
locking
// Generates random numbers.
public class RandomGenerator {
private static RandomGenerator gen = null;
public static RandomGenerator getInstance() {
if (gen == null) {
synchronized (RandomGenerator.class) {
// must test again -- can you see why?
// sometimes called test-and-test-and-set (TTS)
if (gen == null) {
gen = new RandomGenerator();
}
}
}
return gen;
}
}
11
Singleton exercise
Let's make our game model a singleton. What other
classes can be singletons in this system?
Open issue: What happens if we want a saveable
game, where the game state can be stored onto the
disk?
Will there be any issues with this that are unique to a singleton
class?
12
Pattern: Memento
a memory snapshot of an object's state
13
Memento pattern
problem: sometimes we want to hold onto a version of
an important object's state at a particular moment
memento: a saved "snapshot" of the state of an
object or objects for possible later use; useful for:
writing an Undo / Redo operation
ensuring consistent state in a network
persistency; save / load state between executions of program
we'll examine Memento in the context of saving an object to
disk using streams
14
I/O streams, briefly
stream: an abstraction of a source or target of data
bytes "flow" to (output) and from (input) streams
can represent many data sources:
files on hard disk
another computer on network
web page
input device
(keyboard, mouse, etc.)
15
Stream hierarchy
java.io.InputStream
AudioInputStream
FileInputStream
ObjectInputStream
java.io.OutputStream
ByteArrayOutputStream
FileOutputStream
ObjectOutputStream
16
Serialization
serialization: reading /
writing objects and their exact
state using I/O streams
allows objects themselves to be
written to files, across network, to
internet, etc.
lets you save your objects to disk
and restore later
avoids converting object's state
into arbitrary text format
17
Classes used for serialization
in java.io package:
ObjectOutputStream class represents a connection to
which an object can be written / sent (saved)
public class ObjectOutputStream
public ObjectOutputStream(OutputStream out)
public void writeObject(Object o)
throws IOException
ObjectInputStream class represents a connection from
which an object can be read / received (loaded)
public class ObjectInputStream
public ObjectInputStream(InputStream in)
public Object readObject() throws Exception
FileInputStream, FileOutputStream can be
constructed from a file name string
18
Serialization example
recommendation: use a memento class that has save/load code
// write the object named someObject to file "file.dat"
try {
OutputStream os = new FileOutputStream("file.dat");
ObjectOutputStream oos = new ObjectOutputStream(os);
oos.writeObject(someObject);
os.close();
} catch (IOException e) { ... }
// load the object named someObject from file "file.dat"
try {
InputStream is = new FileInputStream("file.dat");
ObjectInputStream ois = new ObjectInputStream(is);
WhateverType someObject = (WhateverType) ois.readObject();
is.close();
} catch (Exception e) { ... }
19
Memento sequence diagram
20
Making your classes serializable
You must implement the (methodless)
java.io.Serializable interface for your class to be
compatible with object input/output streams.
public class BankAccount implements Serializable
{
...
ensure that all instance variables inside your class are either
serializable or declared transient
transient fields won't be saved when object is serialized
21
serialVersionUID
There is a versioning issue with serializing and
deserializing objects.
You might save a BankAccount object, then edit and recompile
the class, and later try to load the (now obsolete) object
Serializable objects should have a field inside named
serialVersionUID that marks the "version" of the code
(you can set it to 0 and never change it, if you don't need to
support multiple versions of the same product)
public class BankAccount implements Serializable
{
private static final long serialVersionUID = 0;
...
22
Back to singleton...
Let's make our (singleton) game model serializable
What can happen if a singleton is saved and loaded?
Is it possible to have more than one instance of the singleton's
type in our system?
Does this violate the Singleton pattern? Will it break our code?
If so, how can we fix it?
23
Singleton example 5
variation: has strict checks to make sure that we have
not saved a stale reference to the singleton object
// Generates random numbers.
public class RandomGenerator {
private static RandomGenerator gen = null;
...
public double nextNumber() {
// put code like this in methods that use/modify it
if (this != gen) {
throw new IllegalStateException("not singleton");
}
return Math.random();
}
}
Is this a good solution?
What do we do if we want to save/load a singleton object?
24
Pattern: Flyweight
a class that has only one instance for each unique state
25
Problem of redundant objects
problem: redundant objects can bog down system
many objects have same state
intrinsic vs. extrinsic state
example: File objects that represent the same file on disk
new
new
new
...
new
new
File("mobydick.txt")
File("mobydick.txt")
File("mobydick.txt")
File("notes.txt")
File("notes.txt")
26
Flyweight pattern
flyweight: an assurance that no more than one
instance of a class will have identical state
achieved by caching identical instances of objects to reduce
object construction
similar to singleton, but has many instances, one for each
unique-state object
useful for cases when there are many instances of a type but
many are the same
can be used in conjunction with Factory pattern to create a very
efficient object-builder
examples in Java: String, Image / Toolkit, Formatter
27
Flyweight and Strings
Flyweighted strings
Java Strings are flyweighted by the compiler in many cases
can be flyweighted at runtime with the intern method
String fly = "fly",
String fly2 = "fly",
weight = "weight";
weight2 = "weight";
Which of the following expressions are true?
fly == fly2
weight == weight2
"fly" + "weight" == "flyweight"
fly + weight == "flyweight"
String flyweight = new String("fly" + "weight");
flyweight == "flyweight"
String interned = (fly + weight).intern();
interned == "flyweight"
28
Implementing a Flyweight
flyweighting works best on immutable objects
immutable: cannot be changed once constructed
class pseudo-code sketch:
public class Flyweighted {
static collection of instances
private constructor
static method to get an instance:
if (we have created this kind of instance before),
get it from the collection and return it
else,
create a new instance, store it in the collection and return it
}
29
Flyweight sequence diagram
30
Implementing a Flyweight
public class Flyweighted {
private static Map instances;
private Flyweighted() {}
public static synchronized Flyweighted getInstance(Object key) {
if (!myInstances.contains(key)) {
Flyweighted fw = new Flyweighted(key);
instances.put(key, fw);
return fw;
} else {
return instances.get(key);
}
}
}
31
Class before flyweighting
A class to be flyweighted
public class Point {
private int x, y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() { return x; }
public int getY() { return y; }
public String toString() {
return "(" + x + ", " + y + ")";
}
}
32
Class after flyweighting
A class that has been flyweighted!
public class Point {
private static Map<String, Point> instances =
new HashMap<String, Point>();
public static Point getInstance(int x, int y) {
String key = x + ", " + y;
if (instances.containsKey(key)) {
// re-use existing pt
return instances.get(key);
}
Point p = new Point(x, y);
instances.put(key, p);
return p;
}
private final int x, y;
// immutable
private Point(int x, int y) {
...
33
References
The Java Tutorial: I/O
Java API pages
http://java.sun.com/j2se/1.4.2/docs/api/java/io/ObjectInputStream.html
http://java.sun.com/j2se/1.4.2/docs/api/java/io/ObjectOutputStream.html
http://java.sun.com/j2se/1.4.2/docs/api/java/awt/FileInputStream.html
http://java.sun.com/j2se/1.4.2/docs/api/java/awt/FileOutputStream.html
Cunningham & Cunningham OO Consultancy, Inc.
http://java.sun.com/docs/books/tutorial/essential/io/
http://c2.com/cgi/wiki?SingletonPattern
http://c2.com/cgi/wiki?MementoPattern
http://c2.com/cgi/wiki?FlyweightPattern
Design Patterns Java Companion
http://www.patterndepot.com/put/8/JavaPatterns.htm
34
Pattern: Factory
(a variation of Factory Method, Abstract Factory)
a class or method used to create objects easily
35
Problem: Bulky GUI code
GUI code to construct many components quickly
becomes redundant (here, with menus):
homestarItem = new JMenuItem("Homestar Runner");
homestarItem.addActionListener(this);
viewMenu.add(homestarItem);
crapItem = new JMenuItem("Crappy");
crapItem.addActionListener(this);
viewMenu.add(crapItem);
another example (with buttons):
button1 = new JButton();
button1.addActionListener(this);
button1.setBorderPainted(false);
button2 = new JButton();
button2.addActionListener(this);
button2.setBorderPainted(false);
36
Factory pattern
factory: A class whose job is to easily create and
return instances of other classes.
a creational pattern; makes it easier to construct complex
objects
instead of calling a constructor, use a static method in a
"factory" class to set up the object
saves lines, complexity to quickly construct / initialize objects
examples in Java: borders (BorderFactory), key strokes
(KeyStroke), network connections (SocketFactory)
37
Using factories in Java
Setting borders on buttons and panels:
use BorderFactory class
myButton.setBorder(
BorderFactory.createRaisedBevelBorder());
Setting hot-key "accelerators" on menus:
use KeyStroke class
menuItem.setAccelerator(
KeyStroke.getKeyStroke('T', KeyEvent.ALT_MASK));
38
Factory implementation
When implementing a factory of your own:
The factory itself should not be instantiated.
The factory uses static methods to construct
components.
The factory should offer as simple an interface to client
code as possible.
make constructor private
Don't demand lots of arguments; possibly overload factory
methods to handle special cases that need more arguments.
Factories are often designed for reuse on a later project
or for general use throughout your system.
39
Factory sequence diagram
40
Factory example
public class ButtonFactory {
private ButtonFactory() {}
public static JButton createButton(
String text, ActionListener listener,
Container panel) {
JButton button = new JButton(text);
button.setMnemonic(text.charAt(0));
button.addActionListener(listener);
panel.add(button);
return button;
}
}
41
GoF's variations on Factory
Factory Method pattern: A factory object that can be
constructed and has an overridable method to create
its objects
can be subclassed to make new kinds of factories
Abstract Factory pattern: When the topmost factory
class and its creational method are abstract (can be
overridden)
42
Pattern: Command
objects that represent actions
43
Open-closed principle
Open-Closed Principle: Software entities like classes,
modules and functions should be open for extension
but closed for modifications.
The Open-Closed Principle encourages software
developers to design and write code in a fashion that
adding new functionality would involve minimal
changes to existing code.
Most changes will be handled as new methods and new classes.
Designs following this principle would result in resilient code
which does not break on addition of new functionality.
44
Command pattern
command: An object that represents an action.
Sometimes called a "functor" to represent an object whose sole
goal is to encapsulate one function
Java API examples:
ActionListener, Comparator, Runnable / Thread
45
Command applications
Command objects serve as a "model" of commands:
separates the user interface from them
each model can support multiple views
each action can support multiple widgets
Use the Command pattern when you want to:
implement a callback function capability
have several UI widgets that cause the same action to occur
specify, queue, and execute requests at different times
support undo and change log operations
46
Example: Bad event handling
public class TTTGui implements ActionListener {
...
public void actionPerformed(ActionEvent event) {
if (event.getSource() == view1Item){
// switch to view #1 ...
else {
// event source must be view2Item
// switch to view #2 ...
}
}
}
in this code, the "master" TTT GUI object is in charge of
all action events in the UI
is this bad?
what could happen if we add another action event source?
47
Common UI commands
It is common for a GUI to have several ways to activate
the same behavior.
Example: toolbar "Cut" button and "Edit / Cut" menu
This is good ; it makes the program flexible for the user.
We'd like to make sure the code implementing these common
commands is not duplicated.
48
Solution: Action objects
Java's Action interface represents a UI command
a UI command has a text name, an icon, an action to run
can define multiple UI widgets that share a common underlying
command by attaching the same Action to them
These Swing components support Action
JButton(Action a)
JCheckBox(Action a)
JRadioButton(Action a)
JToggleButton(Action a)
JMenuItem(Action a)
AbstractAction class implements Action and
maintains an internal map of keys to values
each key represents a name of a property of the action (e.g. "Name")
each value represents the value for that property (e.g. "Save Game")
can be used to ensure that all UI components that share a common UI
action will have the same text, icon, hotkey
49
ActionListener/Action code
reminder: interface ActionListener
public void actionPerformed(ActionEvent e)
interface Action extends ActionListener
adds property enabled
isEnabled() / setEnabled(boolean)
abstract class AbstractAction implements Action
you must still write actionPerformed
50
AbstractAction members
public class AbstractAction implements Action
public
public
public
public
public
public
...
AbstractAction(String name)
AbstractAction(String name, Icon icon)
Object getValue(String key)
boolean isEnabled()
void putValue(String key, Object value)
void setEnabled(boolean enabled)
AbstractAction object maintains an internal map of
keys to values
Each key represents a name of a property of the action (e.g. "Name").
Each value represents the value for that property (e.g. "Save Game").
51
Using Action, example
define a class that extends AbstractAction:
public class CutAction extends AbstractAction {
public CutAction() {
super("Cut", new ImageIcon("cut.gif"));
}
public void actionPerformed(ActionEvent e) {
// do the action here ...
}
}
create an object of this class, attach it to UI objects:
CutAction cut = new CutAction();
JButton cutButton = new JButton(cut);
JMenuItem cutMenuItem = new JMenuItem(cut);
now the same action will occur in both places; also,
changes to the action will be seen on both widgets:
cut.setEnabled(false);
52
Action and Swing
Other uses: properties of the action can be set
cut.putValue(Action.SHORT_DESCRIPTION,
"Cuts the selected text");
cut.putValue(Action.NAME, "Cut");
will use the text label "Cut" for the name of the command
cut.putValue(Action.MNEMONIC_KEY,
new Integer('T'));
will use this label for the tooltip text
will underline 't' in "Cut" as a mnemonic hotkey for the command
cut.putValue(Action. ACCELERATOR_KEY,
KeyStroke.getKeyStroke('X', KeyEvent.CTRL_MASK));
will use Ctrl-X as an accelerator hotkey for the command
53
References
Java API pages
Cunningham & Cunningham OO Consultancy, Inc.
http://c2.com/cgi/wiki?AbstractFactoryPattern
http://c2.com/cgi/wiki?FactoryMethodPattern
http://c2.com/cgi/wiki?CommandPattern
Design Patterns Java Companion
http://java.sun.com/j2se/1.5.0/docs/api/javax/swing/Action.html
http://java.sun.com/j2se/1.5.0/docs/api/javax/swing/AbstractAction.html
http://www.patterndepot.com/put/8/JavaPatterns.htm
Design Pattern Synopses
http://www.mindspring.com/~mgrand/pattern_synopses.htm
54