Design Pattern - Singleton
- a creational pattern
Intent
Ensure a class only has one instance, and provide a global point of access to it.
Motivation
Some classes should have exactly one instance. These classes usually involve the central management of a resource.
Ideas
- just one audio clip should be played at a time
- an object of the class
AudioClipManager
is responsible for playing audio clips- previous clip is stopped before a new one is started
- ensure that there is just one
AudioClipManager
Example (AudioClipManager
)
classDiagram class AudioClipManager { -instance : AudioClipManager -clip : AudioClip -AudioClipManager() +getInstance() : AudioClipManager +play(clip: AudioClip) +stop() ... } AudioClipManager : getInstance() returns instance
Structure & Considerations
classDiagram class Singleton { -instance : Singleton -Singleton() +getInstance() : Singleton ... } Singleton : getInstance() returns instance
- lazy instantiation
- create
instance
at load-time vs postpone creation until the first call ofgetInstance()
- create
- interface
Clonable
- a singleton object should not implement this interface
- if you can use
.clone()
, it is no longer singleton
- Object serialization
- serialization can be used to copy objects
- a singleton object should not implement the interface
Serializable
- can save using serializable and load to another object, essentially having a clone, meaning it is no longer singleton
Consequences
- Controlled access to sole instance
- because the
Singleton
class encapsulates its sole instance, it can have strict control over how and when clients access it
- because the
- Reduced name space
- the singleton pattern is an improvement over global variables
- it acoids polluting the name space with global variables that store sole instances
- Permits refinement of operations and representation
- the
Singleton
class may be subclassed, and it is easy to configure an application with instances of this extended class
- the
- Permits a variable number of instances
- instead of having max=1 instance, you could have max=5 instances
- More flexible than class operations
- Another way to package a singleton’s functionality is to use class operations.
- This approach makes it hard to change a design to allow more than one instance.
Code Example
The class UniversalCounter
should provide a sole instance which
is capable of counting globally calls of the incCount()
method:
- works in a multi-thread environment
- uses lazy instantiation
Additional considerations:
- lazy instantiation and counting in a concurrent environment
public class UniversalCounter {
private static UniversalCounter myInstance = null;
private int count ;
private UniversalCounter() {
count = 0;
}
public static synchronized UniversalCounter getInstance() {
if (myInstance == null) myInstance = new UniversalCounter();
return myInstance;
}
public synchronized void incCount() {
count++;
}
public int getCount() {
return count;
}
}
Design Object - Null Object
- a behavioural pattern
Intent
The null object pattern provides an alternative to using null
to indicate the absence of an object.
Motivation
Using null
to indicate the absence of an object requires a test for null
before calling a method
Ideas
- we want to provide a vfacility which is able to route warnings/error messages to different locations
- dialog box
- a log file
- nowhere at all
- …
- the use of the class/package should not be forced to test for
null
before using it
Structure
classDiagram class Delegator class OperationIF { } class NullOperation class RealOperation Delegator --> OperationIF : uses OperationIF <|-- NullOperation OperationIF <|-- RealOperation
Example
classDiagram class Application class WarningRouter { +routeWarning(String) } class IgnoreWarning class WarningDialog class WarningLogger Application --> WarningRouter : uses WarningRouter <|-- IgnoreWarning WarningRouter <|-- WarningDialog WarningRouter <|-- WarningLogger
public interface WarningRouter {
public void routeWarning(String msg);
}
public class WarningDialog implements WarningRouter {
public void routeWarning(String msg) {
JOptionPane.showMessageDialog(null,
msg,
”Warning”,
JOptionPane.OK_OPTION,
JOptionPane.WARNING_MESSAGE);
}
}
public class WarningLogger implements WarningRouter { /* ... */}
public class IgnoreWarning implements WarningRouter {
public void routeWarning(String msg) {
}
}
XML
- XML stands for EXtensible Markup Language
- XML is a mrkup language much like HTML, but
- all XML elements must have a closing tag
- XML tags are case sensitive
- all XML elements must be properly nested
- all XML documents must have a single root element
- attribute values must always be quoted
- XML was designed to describe data
- XML tags are not predefined. You must define your own tags
Simple Example
<note date="12/11/2002">
<to>Rob</to>
<from>Jani</from>
<heading>Reminder</heading>
<body>Don't forget me this weekend!</body>
</note>
Larger Example
<?xml version="1.0"?>
<Factory xSize="9" ySize="8" maxPlayers="4" numDecorations="8" numWalls="15">
<TilePit/>
<TileFloor/>
<TilePit/>
...
<DecorationCrusher x="1" y="4" phase1="2" phase2="4"/>
<DecorationCrusher x="3" y="6" phase1="1" phase2="3"/>
<DecorationCrusher x="5" y="3" phase1="4" phase2="5"/>
...
<StartConfig x="1" y="0" direction="North"/>
...
<Wall x="1" y="2" isHorizontal="true"/>
<Wall x="5" y="2" isHorizontal="true"/>
</Factory>
XML Schema
XML Schema is a language that can be used ot descript the syntactical structure of XML documents. It is:
- expressed in XML
- self-describing and simple
- can be used to validate XML documents
- W3C approved (World Wide Web Consortium)
A XML document may specify its schema using the schemaLocation
attribute.
<Factory xSize="9" ySize="8" maxPlayers="4" numDecorations="8" numWalls="15" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
In Java, XML parsers can be triggered to use a specific schema.
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
<xs:include schemaLocation="TileBelt.xsd"/>
...
<xs:element name="Factory">
<xs:complexType>
<xs:sequence>
<xs:choice minOccurs="1" maxOccurs="unbounded">
<xs:element ref="TileBelt"/>
...
</xs:choice>
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element ref="DecorationCrusher"/>
<xs:element ref="DecorationPusher"/>
</xs:choice>
<xs:element minOccurs="1" maxOccurs="unbounded" ref="StartConfig"/>
<xs:element minOccurs="0" maxOccurs="unbounded" ref="Wall"/>
</xs:sequence>
<xs:attribute name="xSize" use="required" type="xs:nonNegativeInteger"/>
<xs:attribute name="ySize" use="required" type="xs:nonNegativeInteger"/>
<xs:attribute name="maxPlayers" use="required" type="xs:nonNegativeInteger"/>
<xs:attribute name="numDecorations" use="required" type="xs:nonNegativeInteger"/>
<xs:attribute name="numWalls" use="required" type="xs:nonNegativeInteger"/>
</xs:complexType>
</xs:element>
</xs:schema>
Parsing XML
In order to parse an XML source you’ll need:
javax.xml.parsers.DocumentBuilderFactory
:- a factory creating
DocumentBuilder
s - according to the parameters of the factory generated
DocumentBuilder
will use certain XMLSchemas
- a factory creating
javax.xml.parsers.DocumentBuilder
:- this class will parse the input and create a
Document
- this class will parse the input and create a
- the input - a file of one of the following classes:
File
org.xml.sax.InputSource
Reader
InputStream
String
(url to a file being parsed))
Node
Interface
An XML document is represented by a tree structure, in which each tree node is a class implementing the interface org.w3c.dom.Node
.
Important methods:
String getNodeName()
#document
#text
: example,<to>Rob</to>
is actually a<to>
element with a#text
tag equal to Rob- user defined tag
NamedNodeMap getAttributes()
Attr getNamedItem(String name)
Node getFirstChild()
Node getLastChild()
NodeList getChildNodes()
int length()
Node item(int i)
Java Example
factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
factory.setValidating(true);
try {
factory.setAttribute(JAXP_SCHEMA_LANGUAGE,W3C_XML_SCHEMA);
} catch (Exception ex) { ... };
String schema = "...factory.xsd";
String[] schemas = { schema };
factory.setAttribute(JAXP_SCHEMA_SOURCE,schemas);
try {
builder = factory.newDocumentBuilder();
} catch (Exception ex) { ... };
File f = new File("...factory.xml");
try {
document = builder.parse(f);
} catch (Exception ex) { ... };
COSC3P91.xml
Example
COSC3P91.xml
public interface XMLObject {
public String toXMLString();
}
public interface XMLNodeConverter<E> {
public E convertXMLNode(Node node);
}
COSC3P91.xml.XMLReader
public class XMLReader<E> {
private static final String JAXP_SCHEMA_LANGUAGE =
"http://java.sun.com/xml/jaxp/properties/schemaLanguage";
private static final String W3C_XML_SCHEMA =
"http://www.w3.org/2001/XMLSchema";
private static final String JAXP_SCHEMA_SOURCE =
"http://java.sun.com/xml/jaxp/properties/schemaSource";
private DocumentBuilderFactory factory;
private DocumentBuilder builder = null;
private Document document = null;
private XMLNodeConverter<E> converter = null;
public XMLReader() {
factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
factory.setValidating(true);
try {
factory.setAttribute(JAXP_SCHEMA_LANGUAGE,W3C_XML_SCHEMA);
} catch (Exception ex) {
ex.printStackTrace();
System.exit(1);
};
}
public void setXMLSchema(String schema) {
String[] schemas = { schema };
factory.setAttribute(JAXP_SCHEMA_SOURCE,schemas);
try {
builder = factory.newDocumentBuilder();
} catch (Exception ex) {
ex.printStackTrace();
System.exit(1);
};
}
public void setXMLNodeConverter(XMLNodeConverter<E> converter) {
this.converter = converter;
}
public E readXML(File f) {
checkStatus();
try {
document = builder.parse(f);
} catch (Exception ex) {
ex.printStackTrace();
};
return converter.convertXMLNode(document.getFirstChild());
}
...
private void checkStatus() {
if (builder==null) {
System.out.println("No XMLSchema set.");
System.exit(1);
};
if (converter==null) {
System.out.println("No XMLNodeConverter set.");
System.exit(1);
};
}
COSC3P91.xml.XMLTools
public static List<Node> getChildNodes(Node node) {
List<Node> result = new LinkedList<Node>();
NodeList list = node.getChildNodes();
for(int i=0;i<list.getLength();i++)
if (!list.item(i).getNodeName().equals("#text")) result.add(list.item(i));
return result;
}
public static int getIntAttribute(Node node, String name) {
Attr attr = (Attr) node.getAttributes().getNamedItem(name);
return Integer.valueOf(attr.getValue());
public static boolean getBoolAttribute(Node node, String name) {
Attr attr = (Attr) node.getAttributes().getNamedItem(name);
return Boolean.valueOf(attr.getValue());
}
public static String getStringAttribute(Node node, String name) {
Attr attr = (Attr) node.getAttributes().getNamedItem("name");
return attr.getValue();
}
public static Enum getEnumAttribute(Class c, Node node, String name){
Attr attr = (Attr) node.getAttributes().getNamedItem(name);
Class[] array = new Class[1];
array[0] = String.class;
Object obj = null;
try {
obj = c.getMethod("valueOf",array).invoke(null,attr.getValue());
} catch (Exception e) { ... };
if (obj instanceof Enum) return (Enum) obj;
return null;
}
Example
public class Factory implements XMLObject {
...
public static Factory load(String fileName) {
String xsd = "../XSD/factory.xsd";
XMLReader<Factory> reader = new XMLReader<Factory>();
reader.setXMLSchema(xsd);
reader.setXMLNodeConverter(new FactoryReader());
return reader.readXML(new File(fileName));
}
...
public String toXMLString() {
String result = "<Factory xSize=\"" + xSize + "\" ySize=\"" + ySize +"\" maxPlayers=\"" + robotStartConfigs.length + "\" numDecorations=\"" + decorations.length + "\" numWalls=\"" + walls.length + "\">\n";
for(int j=0; j<ySize; j++) {
for(int i=0; i<xSize; i++) {
result += tiles[i][j].toXMLString() + "\n";
}
}
for (Decoration decoration : decorations) {
result += decoration.toXMLString() + "\n";
}
for(Pair<Point,Direction> config : robotStartConfigs) {
result += "<StartConfig x=\"" + config.getFirst().x + "\" y=\"" + config.getFirst().y + "\" direction=\"" + config.getSecond() + "\"/>\n";
}
for (Wall wall : walls) {
result += wall.toXMLString() + "\n";
}
return result + "</Factory>";
}}
public class FactoryReader implements XMLNodeConverter<Factory> {
private final TileReader tileReader;
private final DecorationReader decorationReader;
...
public Factory convertXMLNode(Node node) {
Factory factory = null;
if (node.getNodeName().equals("Factory")) {
int xSize = getIntAttribute(node,"xSize");
int ySize = getIntAttribute(node,"ySize");
int maxPlayers = getIntAttribute(node,"maxPlayers");
int numDecorations = getIntAttribute(node,"numDecorations");
int numWalls = getIntAttribute(node,"numWalls");
Tile[][] tiles = new Tile[xSize][ySize];
Decoration[] decorations = new Decoration[numDecorations];
Wall[] walls = new Wall[numWalls];
Pair<Point,Direction>[] robotStartConfigs =
(Pair<Point,Direction>[]) new Pair<?,?>[maxPlayers];
List<Node> list = getChildNodes(node);
for(int j=0; j<ySize; j++) {
for(int i=0; i<xSize; i++) {
tiles[i][j] = tileReader.convertXMLNode(list.get(j*xSize+i));
}
}
for(int i=0; i<numDecorations; i++) {
decorations[i] = decorationReader.convertXMLNode(list.get(xSize*ySize+i));
}
...
return factory;
}
}
Multithreading
- Thread
- single sequential flow of control
- managed by the same Java virtual machine
- thread versus process
- managed by the operating system
- no shared memory space
- communication just via interprocess communication channels
- advantages
- reactive systems
- continuously monitor array of sensors and react according to the sensor readings
- reactive GUI
- allows to respond to user input immediately even if the application is engaged in a time-consuming task
- multi-client servers
- multiple processors
- executing threads on different processors in parallel
- reactive systems
- nondeterministic thread ordering
- multi processors
- time-sharing
Creating and Running Threads
Four possibilities:
- extending the
Thread
class - implementing the
Rubbable
interface - using anonymous inner classes
- using a lambda expression
Thread
class
- class for active objects
- subclasses of
Thread
should override therun
methodrun
hook method
- implements the interface
Runnable
- implement
run
- implement
- subclasses of
start
methodrun
should not be invoked directly. Doing so would cause the method to be executed in the thread of the caller, not in a new thread- use the start method to execute a thread
Extending the Thread
class
public class MyThread extends Thread {
public void run() {
System.out.println(“Do something cool here.“);
}
}
Thread myThread = new MyThread();
myThread.start();
Implementing the Runnable
interface
public class MyClass extends SomeOtherClass
implements Runnable {
public MyClass() {
Thread thread = new Thread(this);
thread.start();
}
public void run() {
System.out.println(“Do something cool here.“);
}
}
Using anonymous inner classes or a lambda expression
new Thread() {
public void run() {
System.out.println(“Do something cool here.“);
}
}.start();
or
new Thread(() ->
System.out.println(“Do something cool here.“)).start();
Comparison of the methods
- Extending the
Thread
class is easy but uses inheritance - Use the
Runnable
interface if you want to hae a class that extends another and can also run as a thread - Use anonymous inner classes or lambda expressions only if the code in the
run
method is very short