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 of getInstance()
  • 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
  • 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
  • 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 DocumentBuilders
    • according to the parameters of the factory generated DocumentBuilder will use certain XMLSchemas
  • javax.xml.parsers.DocumentBuilder:
    • this class will parse the input and create a Document
  • 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
  • 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 the run method
      • run hook method
    • implements the interface Runnable
      • implement run
  • start method
    • run 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