Creational Patterns
Singleton Pattern
A singleton pattern is an object creational pattern that allows our application to create one and only one instance of a particular class, no matter how many times that class is used in our application. For example a PropertyReader class that can read the properties from a file and it is used multiple times in our application by different classes. We will create only one object of the PropertyReader and not multiple saving a lot of memory using the singleton pattern. So all application classes will use the same property reader object to perform the property reading from a file.
Another good example for singleton is a logger. Using logger's we can log different types of information errors debugging information and just general information from our application to a log file so that developers can read that log file later on to see what is happening with our application. So a logger can be a singleton the same logger object can be shared across our application classes.
Another good example in the JDBC world, the java database connectivity world is the data source class in the JDBC API the data source class is responsible for maintaining a connection pool and giving a connection from the pool to our application classes. We can have different classes in the application that need a database connection but there will be only one instance of the data source. So a DataSource is also an example of singleton.
package com.sudhir.patterns.singleton;
import java.io.Serializable;
public class DateUtil implements Serializable,Cloneable {
private static final long serialVersionUID = 1L;
private static volatile DateUtil instance;
private DateUtil() {
}
public static DateUtil getInstance() {
if (instance == null) {
synchronized (DateUtil.class) {
if (instance == null) {
instance = new DateUtil();
}
}
}
return instance;
}
protected Object readResolve() {
return instance;
}
@Override
protected Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException();
}
}
------
package com.sudhir.patterns.singleton;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class Test {
public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
DateUtil dateUtil1 = DateUtil.getInstance();
ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream(new File("/Users/sudhir/Documents/singleton/dateUtil.txt")));
oos.writeObject(dateUtil1);
DateUtil dateUtil2 = null;
ObjectInputStream ois = new ObjectInputStream(
new FileInputStream(new File("/Users/sudhir/Documents/singleton/dateUtil.txt")));
dateUtil2 = (DateUtil) ois.readObject();
oos.close();
ois.close();
System.out.println(dateUtil1 == dateUtil2);
}
}
Factory Pattern
A factory pattern is a creational pattern that abstracts or hides the object creation process. When you think of factory you can think of a car factory a chocolate factory or a toy factory.A car factory is responsible for manufacturing the cars .A car dealer need not worry about how the car is manufactured. He simply asks the car factory to deliver him some cars. The car factory is responsible for manufacturing them and delivering them to the dealer.
Similarly the Chocolate Factory delivers different types of chocolates based on what the chocolate store asks them to deliver.
An example from the JDBC space when we use different databases like Oracle, MySql, SqlServer. In Java we use something called JDBC driver to connect to a database.
Driver is an interface in the JDBC API and the implementation for this driver is provided by different vendors. The responsibility of the driver is to connect to a particular database and execute sql statements against it.
To get a connection we need not remember each and every driver in how it works. For example if we want to connect to Oracle you need not deal with the Oracle driver and same for other databases. You simply use DriverManager which is a class in the JDBC API to get connection and you pass in a connection string which is specific to a particular database where it is different for mysql, it's different for sql server.
The driver manager acts as a factory and it will return the connection for us by using the appropriate driver for Oracle it will use Oracle driver for my sql it will use the my sql driver and so on. It hides all the details of finding a driver and creating a connection against the database for us. So give it a string it will give you a connection. So here the get connection is a static method on the factory class.
We need not create the instance of the driver manager to create objects of type connection. We simply use the static method on the factory class and get the object we need.
Another example is a pizza store .A pizza store delivers different types of pizzas.We will a parent interface which is implemented by the veg pizza cheese pizza and meat pizza.The pizza store need not worry about how to create each of these pizzas.It simply asked the pizza factory to deliver the type of pizza it wants or to create a type of pizza it wants.so that it can give it to the customer or it can deliver it to the customer.The pizza factory hides the complexity of creating the different types of pizzas from the pizza store.
package com.sudhir.patterns.factory;
public class VeggiePizza implements Pizza {
@Override
public void prepare() {
System.out.println("Preparing Veggie Pizza");
}
@Override
public void bake() {
System.out.println("Baking Veggie Pizza");
}
@Override
public void cut() {
System.out.println("Cutting Veggie Pizza");
}
}
----
package com.sudhir.patterns.factory;
public class Test {
public static void main(String[] args) {
PizzaStore ps = new PizzaStore();
ps.orderPizza("chicken");
}
}
-----
package com.sudhir.patterns.factory;
public class PizzaStore {
public Pizza orderPizza(String type) {
Pizza p = PizzaFactory.createPizza(type);
p.prepare();
p.bake();
p.cut();
return p;
}
}
----
package com.sudhir.patterns.factory;
public class PizzaFactory {
public static Pizza createPizza(String type) {
Pizza p = null;
if (type.equals("cheese")) {
p = new CheesePizza();
} else if (type.equals("chicken")) {
p = new ChickenPizza();
} else if (type.equals("veggie")) {
p = new VeggiePizza();
}
return p;
}
}
-----
package com.sudhir.patterns.factory;
public interface Pizza {
void prepare();
void bake();
void cut();
}
-----
package com.sudhir.patterns.factory;
public class ChickenPizza implements Pizza {
@Override
public void prepare() {
System.out.println("Preparing Chicken Pizza");
}
@Override
public void bake() {
System.out.println("Baking Chicken Pizza");
}
@Override
public void cut() {
System.out.println("Cutting Chicken Pizza");
}
}
----
package com.sudhir.patterns.factory;
public class CheesePizza implements Pizza {
@Override
public void prepare() {
System.out.println("Preparing Cheese Pizza");
}
@Override
public void bake() {
System.out.println("Baking Cheese Pizza");
}
@Override
public void cut() {
System.out.println("Cutting Cheese Pizza");
}
}
Abstract Factory
Now that you have mastered the Factory design pattern learning and implementing the Abstract Factory pattern will be quite easy because an abstract factory is a factory of factories.That is a factory pattern was hiding the details of object creation and factory of factories or an abstract factory.hides the creation of the factory itself.
A good example in the Java space is the JAXP API . JAXP stands for Java API for xml parsing . Using this API we can read or write and update the elements in a xml file the key class in this API is the document class that represents a xml document in memory to create a document class.We use that document builder.So this document builder is a factory class and there is one more class document.Build a factory which is responsible for creating the document builder itself.So the document builder factory is a abstract factory because it is a factory of factories and the use case you are going to work on to give you Another example is a DAO factory. DAO stands for data access object.We'll learn more about it in sections later on.It simply is a class that can read write create update data we can have different types of DAO'sDAO's that deal with xml data and DAO's that deal with DB data and within xml we can have employee information department information.
Similarly within that database we can have employ information and department information.So you can see that we can have a factory to deal with these separate DOA's we can have a DB DAO factory that can give us one of these classes here when our application needs them and we can have a xml DAO factory which can give one of the classes here.Now to get one of these factories themselves these factories will first implement a DAO abstract factory or they will extend DAO abstract factory class and we will have a producer which is responsible for creating one of these factories so abstract factory is a factory off factories. It simply creates the factory we need. when we have multiple factories we see in our application.
package com.sudhir.patterns.abstractfactory;
public interface Dao {
void save();
}
-----
package com.sudhir.patterns.abstractfactory;
public class XMLEmpDao implements Dao {
@Override
public void save() {
System.out.println("Saving Employee to XML");
}
}
-----
package com.sudhir.patterns.abstractfactory;
public class XMLDeptDao implements Dao {
@Override
public void save() {
System.out.println("Saving Department to XML");
}
}
-----
package com.sudhir.patterns.abstractfactory;
public class XMLDaoFactory extends DaoAbstractFactory {
@Override
public Dao createDao(String type) {
Dao dao = null;
if (type.equals("emp")) {
dao = new XMLEmpDao();
} else if (type.equals("dept")) {
dao = new XMLDeptDao();
}
return dao;
}
}
-----
package com.sudhir.patterns.abstractfactory;
public abstract class DaoAbstractFactory {
public abstract Dao createDao(String type);
}
-----
package com.sudhir.patterns.abstractfactory;
public class DaoFactoryProducer {
public static DaoAbstractFactory produce(String factoryType) {
DaoAbstractFactory daf = null;
if (factoryType.equals("xml")) {
daf = new XMLDaoFactory();
} else if (factoryType.equals("db")) {
daf = new DBDaoFactory();
}
return daf;
}
}
-----
package com.sudhir.patterns.abstractfactory;
public class DBDaoFactory extends DaoAbstractFactory {
@Override
public Dao createDao(String type) {
Dao dao = null;
if (type.equals("emp")) {
dao = new DBEmpDao();
} else if (type.equals("dept")) {
dao = new DBDeptDao();
}
return dao;
}
}
-----
package com.sudhir.patterns.abstractfactory;
public class DBDeptDao implements Dao {
@Override
public void save() {
System.out.println("Saving Department to DB");
}
}
-----
package com.sudhir.patterns.abstractfactory;
public class DBEmpDao implements Dao {
@Override
public void save() {
System.out.println("Saving Employee to DB");
}
}
-----
package com.sudhir.patterns.abstractfactory;
public class Test {
public static void main(String[] args) {
DaoAbstractFactory daf = DaoFactoryProducer.produce("db");
Dao dao = daf.createDao("dept");
dao.save();
}
}