Factory Method and Singleton Patterns

Definition

Factory Method

The Factory Method Pattern defines an interface for creating an object, but lets subclass decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.

image

Figure 01. Class diagram of the Factory Method. Ref:http://en.wikipedia.org/wiki/Factory_method_pattern

Singleton

The Singleton Pattern ensures a class has only one instance, and provides a global point of access to it.

image

Figure 02. Singleton class Ref: http://en.wikipedia.org/wiki/Singleton_pattern

Scenario

One of the applications of the Singleton Pattern is in a Factory class, due to the fact that factories don’t need to be instantiated multiple times and just one factory can create as many objects as required, we can save memory in the system making the Factory a Singleton.

For this reason we will create a small factory that will create two (or can be more) types of users:

  • A simple user
  • A super user

What is going to differentiate these users is going to be the rights attached to them:

  • A simple user will have these rights: Review and Edit customers.
  • A super user will have these rights: Remove and Create customers.

For the design of the proposed study case we will refer to the following class diagram:

image

Figure 03. Class diagram of the solution proposed

As it can be seen in this figure, we will have two factories that will create a specific type of user, and an abstract factory that will set all the common data for both factories that extends the abstract class UserWithRightsFactory.

UserWithRightsFactory.java

package chris.desingpatterns.factorysingleton.factory;

import java.util.ArrayList;
import java.util.List;

import chris.desingpatterns.factorysingleton.server.pojos.Right;

public abstract class UserWithRightsFactory {

protected abstract User getNewUser();

public User createUser(){
User user = getNewUser();

List<Right> rights = new ArrayList<Right>();

setBasicRights(rights);

if(user instanceof SuperUser){
setSuperRights(rights);
}

user.setRights(rights);
return user;
}

private void setBasicRights(List<Right> rights){
//Basic rights
rights.add(Right.REVIEW_CUSTOMER);
rights.add(Right.EDIT_CUSTOMER);
}

private void setSuperRights(List<Right> rights){
//Super rights
rights.add(Right.REMOVE_CUSTOMER);
rights.add(Right.CREATE_CUSTOMER);
}

}
The specific factories that will create new Customer objects, are the ones that will implement the Singleton Pattern; the main characteristic of a singleton class is that there is an static method that is going to verify if an instance of this class was already created or not, if is the case that the instance of the singleton was already created, then this static method will just return the existing singleton object, otherwise it will create a new instance of the singleton; usually the name of this method in a Singleton class is: getInstance().
 
In the following snippet it can be seen how a singleton is implemented.
 
SimpleUserRightsFactory.java
 
package chris.desingpatterns.factorysingleton.factory;


public class SimpleUserRightsFactory extends UserWithRightsFactory {

private static SimpleUserRightsFactory uniqueInstance;

private SimpleUserRightsFactory() {}

public static SimpleUserRightsFactory getInstance(){
if(uniqueInstance == null){
uniqueInstance = new SimpleUserRightsFactory();
}

return uniqueInstance;
}

protected User getNewUser() {
return new User();
}

}
And the class that will run our scenario:
 
Main.java
 

package chris.desingpatterns.factorysingleton;

import java.util.ArrayList;
import java.util.List;

import chris.desingpatterns.factorysingleton.factory.SimpleUserRightsFactory;
import chris.desingpatterns.factorysingleton.factory.SuperUser;
import chris.desingpatterns.factorysingleton.factory.SuperUserRightsFactory;
import chris.desingpatterns.factorysingleton.factory.User;
import chris.desingpatterns.factorysingleton.factory.UserWithRightsFactory;
import chris.desingpatterns.factorysingleton.server.pojos.Address;
import chris.desingpatterns.factorysingleton.server.pojos.ContactInformation;
import chris.desingpatterns.factorysingleton.server.pojos.Identification;
import chris.desingpatterns.factorysingleton.server.pojos.IdentificationDocument;
import chris.desingpatterns.factorysingleton.server.pojos.Name;

public class Main {

public static void main(String... args){
Name name = new Name();
name.setFirstName("Christian");
name.setLastName("Roman");

Address address = new Address();
address.setCity("La Paz");
address.setCountry("Bolivia");
address.setHouseNumber("333");
address.setStreet("Ocobaya");

Identification identifcation = new Identification();
identifcation.setDocument(IdentificationDocument.ID);
identifcation.setNumber("123456789");

ContactInformation contactInformation = new ContactInformation();
contactInformation.setEmail("cralcubo@yahoo.com");
contactInformation.setTelephone("0652630333");

User simpleUser = createSimpleUser(name, address,
identifcation, contactInformation);

Name nameB = new Name();
nameB.setFirstName("Karl");
nameB.setLastName("Taylor");

Address addressB = new Address();
addressB.setCity("Utrecht");
addressB.setCountry("Netherlands");
addressB.setHouseNumber("2");
addressB.setStreet("Zijdebalenstraat");

Identification identificationB = new Identification();
identificationB.setDocument(IdentificationDocument.DRIVING_LICENSE);
identificationB.setNumber("ABCD1234564");

ContactInformation contactInformationB = new ContactInformation();
contactInformationB.setEmail("karl@taylor.com");
contactInformationB.setTelephone("0612345678");

User simpleUserB = createSimpleUser(nameB, addressB,
identificationB, contactInformationB);

Name nameC = new Name();
nameC.setFirstName("Anna");
nameC.setLastName("Korte");

Address addressC = new Address();
addressC.setCity("Groningen");
addressC.setCountry("Netherlands");
addressC.setHouseNumber("568");
addressC.setStreet("Boliviastraat");

Identification identificationC = new Identification();
identificationC.setDocument(IdentificationDocument.PASSPORT);
identificationC.setNumber("9875631XX");

ContactInformation contactInformationC = new ContactInformation();
contactInformationC.setEmail("anna@gmail.com");
contactInformationC.setTelephone("0652369875");

User superUser = createSuperUser(nameC, addressC,
identificationC, contactInformationC);
List<User> usersManged = new ArrayList<User>();
usersManged.add(simpleUser);
usersManged.add(simpleUserB);
((SuperUser)superUser).setUsersManged(usersManged );

//Optionally you could print the info of all the users
//created by the factories.
//printAllUsersinformation(simpleUser, simpleUserB, superUser);


}

/**
* Method that will print all the information of the
* users created in the Main method.
*
* @param users
*/
private static void printAllUsersinformation(User ...users) {
for(User user : users){
String userType = (user instanceof SuperUser) ? "SUPER USER" : "USER";
System.out.println(".:. " + userType + " .:.");
System.out.println(user.toString());
if(user instanceof SuperUser){
System.out.println("Users Managed: ");
for(User simpleUser : ((SuperUser)user).getUsersManged()){
System.out.println("- " + simpleUser.getName().getFirstName());
}
}
}

}

/**
* Method that will create a Simple User.
*
* @param name
* @param address
* @param identification
* @param contactInformation
* @return
*/
private static User createSimpleUser(Name name,
Address address,
Identification identification,
ContactInformation contactInformation){

UserWithRightsFactory simpleUserRightsFactory =
SimpleUserRightsFactory.getInstance();
return createUser(simpleUserRightsFactory, name, address,
identification, contactInformation);
}

/**
* Method that will create a Super User.
*
* @param name
* @param address
* @param identification
* @param contactInformation
* @return
*/
private static User createSuperUser(Name name,
Address address,
Identification identification,
ContactInformation contactInformation){

UserWithRightsFactory superUserRightsFactory =
SuperUserRightsFactory.getInstance();
return createUser(superUserRightsFactory, name, address,
identification, contactInformation);
}

/**
* General Method that will create any type of User.
*
* @param factory
* @param name
* @param address
* @param identification
* @param contactInformation
* @return
*/
private static User createUser(UserWithRightsFactory factory,
Name name,
Address address,
Identification identification,
ContactInformation contactInformation){

System.out.println("Factory class: " + factory +
" created user: " + name.getFirstName());

User user = factory.createUser();
user.setName(name);
user.setAddress(address);
user.setIdentification(identification);
user.setContactInformation(contactInformation);

return user;
}

}
That is it!
 
To download the code click in the following link:
 
Do you have any recommendations or amendments to be done, please let me know to improve this post!

Leave a comment