An introduction to creational design patterns in object orientation. Suitable for intermediate to advanced computing students and those studying software engineering.
2. Introduction
ï Today we introduce a small suite of design
patterns that fall under the family known as
creational.
ï They are used to create objects, or manage the
object creation process in some way.
ï For some applications and contexts, itâs not
appropriate or useful to simply instantiate
objects with new whenever you want to.
ï Creational patterns provide a cohesive
interface when these circumstances arise.
3. Creational Patterns
ï There are three creational patterns we will
discuss during this lecture.
ï The Factory
ï The Abstract Factory
ï The Singleton
ï We will also discuss specific examples of
use for each.
4. Why Use A Creational Pattern?
ï Some situations are more complex than
simple instantiation can handle.
ï Imagine for example you want to create an
entirely âskinnableâ look and feel for an
application.
ï Some situations have complex consequences
if objects arenât instantiated in the right way
or the right order.
ï Some situations require that only one object is
ever created.
5. The Factory Pattern
ï The Factory is used to provide a consistent
interface to setup properly configured
objects.
ï You pass in some configuration details
ï Out comes a properly configured object.
ï At its simplest, it can be represented by a
single class containing a single static method.
ï More complex factories exist, dealing with more
complex situations.
8. The Factory Design Pattern
ï Now imagine you are creating a simple
drawing package.
ï User selects a shape
ï User clicks on the screen
ï Application draws the shape.
ï This can all be hard-coded directly into an
application.
ï This suffers from scale and readability issues.
9. The Factory Design Pattern
ï Instead, we use a factory to generate specific
objects, through the power of polymorphism.
ï Polymorphism is key to the way a Factory works.
ï The system that drives a factory is that all
these shapes have a common parent class.
ï Thus, all we need is the Shape object that is
represented by specific objects.
ï The objects themselves manage the
complexity of the drawing process.
10. The Factory Design Pattern
public class ShapeFactory {
public Shape getShape (String shape, int x, int y, int len, int ht, Color col) {
Shape temp = null;
if (shape.equals ("Circle")) {
temp = new Circle (x, y, len, ht);
}
else if (shape.equals ("Rectangle")) {
temp = new Rectangle (x, y, len, ht);
}
else if (shape.equals ("Face")) {
temp = new Face (x, y, len, ht);
}
temp.setDrawingColor (col);
return temp;
}
}
11. Another Example
ï Letâs say we have a file that we have
created in our application.
ï We now want to export it to a different file
format.
ï Each file format has its own peculiarities.
ï We could hard-code this into our
applicationâŠ
ï ⊠or we could use a factory to get the
object that can handle the export.
12. The Factory Design Pattern
public String doConversion (string format, string file) {
ConversionObject c;
c = ConversionFactory.getConversionObject (format);
file = c.covert (file);
return file;
}
myFile = doConversion (âunicodeâ, myFile);
myFile = doConversion (âasciiâ, myFile);
13. The Factory Design Pattern
ï The Factory Pattern reduces hard-coded
complexity.
ï We donât need to worry about combinatorial
explosion.
ï The Factory Pattern properly devolves responsibility
to individual objects.
ï We donât have a draw method in our application,
we have a draw method in each specific shape.
ï However, the Factory pattern by itself is limited to
certain simple contexts.
ï For more complicated situations, we need more.
14. The Abstract Factory
ï The next level of abstraction is the
Abstract Factory.
ï This is a Factory for factories.
ï Imagine here we have slightly more
complicated situations.
ï Designing an interface that allows for
different themes.
ï A file conversion application that must
allow for different versions of different
formats.
15. The Abstract Factory
ï We could handle these with a factory by
itself.
ï This introduces the same combinatorial
problems that the factory is designed to resolve.
ï A simple rule to remember is â coding
combinations is usually bad design.
ï Bad design causes trouble later on.
ï When doing anything more substantial than
simple âproof of conceptâ applications.
16. Bad Design
public Component getComponent (String type, String theme) {
Component guiComponent;
if (theme.equals ("swing")) {
if (type.equals ("button")) {
guiComponent = new JButton ();
}
else if (type.equals ("label")) {
guiComponent = new JLabel();
}
}
else if (theme.equals ("awt")) {
if (type.equals ("button")) {
guiComponent = new Button ();
}
else if (type.equals ("label")) {
guiComponent = new Label();
}
}
return guiComponent;
}
17. Good Design
ï Good Design in this case involves creating
one factory that creates the right kind of
factory for the components.
ï We have a SwingFactory and an AwtFactory.
ï That factory generates the appropriate
components.
ï This requires a somewhat more complicated
class structure.
ï Each Factory must inherit from a common base
19. Abstract Factory
Implementation
public class AbstractFactory {
public static Factory getFactory (string look) {
Factory temp;
if (look.equals ("windows")) {
temp = new WindowsFactory();
}
else if (look.equals ("swing")) {
temp = new SwingFactory();
}
else if (look.equals ("macintosh")) {
temp = new MacintoshFactory();
}
return temp;
}
}
20. Factory Implementation
public class SwingFactory extends Factory {
public GuiWidget getWidget (string type) {
SwingWidget temp = null;
if (type.equals ("button")) {
temp = new JButton();
}
else if (type.equals ("scrollbar")) {
temp = new JScrollbar();
}
return temp;
}
abstract class Factory {
abstract GuiWidget getWidget (String type);
}
21. The Consequence
ï Entirely new suites of themes can be
added to this system without risking
combinatorial explosion.
ï The âoperationalâ code is also much tighter
and more focused.
Factory myFactory = AbstractFactory.getFactory ("swing");
GUIWidget myWidget = myFactory.getWidget ("button");
22. The Singleton
ï The Factory and Abstract Factory handle
structural creation issues.
ï They fix several aspects of bad design with
regards to creating objects.
ï The Singleton is designed to increase data
consistency.
ï One of the problems that must be managed
with object orientation is inter-object
communication.
ï The way this is often done is by giving each
object its own instantiations of other objects.
23. The Singleton
ï This is fine in most situations.
ï However, when dealing with objects that
contain âlive dataâ, it becomes
problematic.
ï Each object has its own copy of the data.
ï Thatâs bad voodoo, man.
ï It would be much better if all objects had
access to the same copy of the data.
ï That is hard to manage effectively.
24. The Singleton
ï The Singleton pattern resolves this by ensuring a
single interface to the creation of an object.
ï Objects cannot be created with new, they must
be created through a method.
ï This method is responsible for ensuring only one live
version of an object.
ï If one exists, it sends that out.
ï If it doesnât, it creates it and then sends it out.
ï The pattern is very simple.
ï But offers great improvements in data consistency.
25. The Singleton
public class Singleton {
private Singleton keepItSafe;
private Singleton() {
// Private constructor prevents external instantiation.
}
public static Singleton getInstance() {
if (keepItSafe == null) {
keepItSafe = new Singleton();
}
return keepItSafe;
}
}
26. The Singleton
ï There are various other ways of implementing
the singleton.
ï As there are other ways of implementing any
design pattern.
ï One way is to keep a static counter of how
many instances have been created.
ï For singleton, if the counter is 1 you donât allow
new objects.
ï The Multiton pattern keeps an internal hash
map of instances.
ï Allowing only newly keyed instantiations.
27. Summary
ï Creational Patterns manage the complexity
of object instantiations.
ï They make it easier to manage the
combinatorial explosion that comes along with
certain kinds of object creation schemes.
ï The Factory allows for the creation of
properly configured objects.
ï The Abstract Factory is a factory for factories.
ï The Singleton ensures data consistency by
restricting instantiation of objects.