Singleton pattern
Singleton pattern restricts the instantiation of a class and ensures that only one instance of the class exists in the java virtual machine.
So instead of doing
Worker worker = new Worker();
in your client. You should be doing this.
Worker worker = Worker.getInstance();
worker.doWork();
worker = Worker.getInstance();
worker.doWork();
Eager Initialization - the instance of Singleton Class is created at the time of class loading
class Worker {
private static final Worker instance = new Worker();
private int counter = 0;
private Worker() {
println("worker initialized");
}
public static Worker getInstance() {
return instance;
}
public void doWork() {
println("counter: "+counter++);
}
}
Static block Initialization - another way of eager initialization, the instance of Singleton Class is created at the time of class loading
class Worker {
private static final Worker instance;
private int counter = 0;
static {
try {
instance = new Worker();
} catch (Exception e) {
throw new RuntimeException("Exception occured in creating singleton instance");
}
}
private Worker() {
println("worker initialized");
}
public static Worker getInstance() {
return instance;
}
public void doWork() {
println("counter: "+counter++);
}
}
Lazy Initialization
class Worker {
private static Worker instance;
private int counter = 0;
private Worker() {
println("worker initialized");
}
public static Worker getInstance() {
if(instance == null) {
instance = new Worker();
}
return instance;
}
public void doWork() {
println("counter: "+counter++);
}
}
Thread safe singleton
class Worker {
private static Worker instance;
private int counter = 0;
private Worker() {
println("worker initialized");
}
public static synchronized Worker getInstance() {
if(instance == null) {
instance = new Worker();
}
return instance;
}
public void doWork() {
println("counter: "+counter++);
}
}
Thread safe - double checked locking - it avoid unnecessary wait on the lock on the method
class Worker {
private static Worker instance;
private int counter = 0;
private Worker() {
println("worker initialized");
}
public static Worker getInstance() {
if(instance == null) {
synchronized (Worker.class) {
if(instance == null) {
instance = new Worker();
}
}
}
return instance;
}
public void doWork() {
println("counter: "+counter++);
}
}
Initialization-On-Demand Holder (Bill Pugh)
Prior to Java 5, java memory model had a lot of issues and above approaches used to fail in certain scenarios where too many threads try to get the instance of the Singleton class simultaneously.
This is lazy at the sametime thread safe.
As the class initialization phase is guaranteed by the JLS to be serial,
i.e., non-concurrent, so no further synchronization is required in the static getInstance() method during loading and initialization.
class WorkerHelper {
public static Worker getInstance() {
return Worker.instance;
}
private static class Worker {
private int counter = 0;
private static final Worker instance = new Worker();
private Worker() {
println("worker initialized");
}
public void doWork() {
println("counter: "+counter++);
}
}
}
Reflection can be used to destroy all the above singleton implementation approaches but enums overcome this situation
Enums - Singletons on steroids
enum Worker {
instance;
private int counter=0;
public void doWork() {
println("counter: "+counter++);
}
}
Factory pattern
Client:
Animal dog = AnimalFactory.createAnimal("Dog"); println(dog.toString());
Factory and implementation:Output:class AnimalFactory { public static Animal createAnimal(String type) { Animal animal = null; if("Dog".equals(type)){ animal = new Dog("Boxi"); return animal; } return animal; } } abstract class Animal { private String name; public abstract String getType(); public String toString(){ return "Type: "+getType()+", Name: "+getName(); } public String getName() { return name; } public void setName(String name) { this.name = name; } } class Dog extends Animal { Dog(String name){ setName(name); } @Override public String getType() { return "Dog"; } }
[main] Type: Dog, Name: BoxiExamples within JDK: Calender ResourceBundle NumberFormat Abstract Factory pattern Client:Factory and impl:AnimalFactory factory = new DogFactory(); Animal dog = factory.createAnimal(); println(dog.toString());
Examples within JDK: javax.xml.parsers.DocumentBuilderFactory javax.xml.transform.TransformerFactory javax.xml.xpath.XPathFactory Builder pattern when the Object contains a lot of attributes. Using default values and lots of setters in factory pattern gets complex or sometimes inconsistent. Client:interface AnimalFactory { Animal createAnimal(); } class DogFactory implements AnimalFactory { public Animal createAnimal(){ return new Dog("Boxi"); } } abstract class Animal { private String name; public abstract String getType(); public String toString(){ return "Type: "+getType()+", Name: "+getName(); } public String getName() { return name; } public void setName(String name) { this.name = name; } } class Dog extends Animal { Dog(String name){ setName(name); } @Override public String getType() { return "Dog"; } }
Dog dog = new DogBuilder().setColor("black").setWeight("10kg").build(); println(dog.toString());
JDK: java.lang.StringBuilder#append() java.lang.StringBuffer#append() Prototype pattern when the Object creation is a costly affair and requires a lot of time and resources and you have a similar object already existing. So this pattern provides a mechanism to copy the original object to a new object and then modify it according to our needs. Client:abstract class Animal { private String name; public abstract String getType(); public String toString(){ return "Type: "+getType()+", Name: "+getName(); } public String getName() { return name; } public void setName(String name) { this.name = name; } } class Dog extends Animal { private String color; private String weight; //Dog class exposes only one public constructor and that takes in a DogBuilder //Any other constructor is usually made private Dog(DogBuilder builder) { this.color = builder.color; this.weight = builder.weight; } @Override public String getType() { return "Dog"; } public String getColor() { return color; } public String getWeight() { return weight; } public String toString(){ return super.toString()+", Color: "+getColor()+", Weight: "+getWeight(); } //We don't provide the setters on the Dog class //As that is taken care by the DogBuilder //any other business logic related to building a Dog class goes into it's builder static class DogBuilder { String color; String weight; public DogBuilder setColor(String color) { this.color = color; return this; } public DogBuilder setWeight(String weight) { this.weight = weight; return this; } public Dog build() { return new Dog(this); } } }
Animals animals = new Animals(); animals.loadNames(); println(animals.getNames()); Animals new_animals = (Animals)animals.clone(); //you don't have to call loadNames again because that data //too is cloned into the object println(new_animals.getNames());
class Animals implements Cloneable { private List
names; Animals() { names = new ArrayList (); } public String toString(){ return "Name: "+getNames(); } public List getNames() { return names; } public void setNames(List names) { this.names = names; } public void loadNames() { //reads data from some data source like DB into list names.add("boxi"); names.add("peggy"); names.add("blacky"); } @Override public Object clone() throws CloneNotSupportedException { Animals animals = new Animals(); //this stores the same reference to list - don't do this - this is not deep copy //animals.setNames(names); //this is deep copy List temp = new ArrayList (); for(String name: getNames()){ temp.add(name); } animals.setNames(temp); return animals; } }
No comments:
Post a Comment