Tuesday, August 30, 2016

Design patterns: Singleton, Immutable object, Builder and Factory

A design pattern is an established general solution to a commonly occurring software development problem. The purpose of a design pattern is to leverage the wealth of knowledge of developers who have come before you in order to solve old problems that you might encounter easily. It also gives developers a common vocabulary in which they could discuss common problems and solutions. For example, if you say that you wrote getters/ setters or implemented the singleton pattern, most developers will understand the structure of your code without having to get into the low‐level details.

Singleton Pattern

The singleton pattern is a creational pattern focused on creating only one instance of an object in memory within an application, sharable by all classes and threads within the application. The globally available object created by the singleton pattern is referred to as a singleton. Singletons might also improve performance by loading reusable data that would otherwise be time-consuming to store and reload each time it is needed.
The following VisitorTicketTracker class represents an example of an implementation of the Singleton Pattern:

// Lazy instantiation
public class VisitorTicketTracker {
private static volatile VisitorTicketTracker instance;

private VisitorTicketTracker() {
}

public static VisitorTicketTracker getInstance() {
if (instance == null) {
synchronized (VisitorTicketTracker.class) {
if (instance == null) {
instance = new VisitorTicketTracker();
}
}
}
return instance;
}

// Data access methods
}

Immutable Objects

The immutable object pattern is a creational pattern based on the idea of creating objects whose state does not change after they are created and could be easily shared across multiple classes. Immutable objects go hand and hand with encapsulation, except that no setter methods exist that modify the object. Since the state of an immutable object never changes, they are inherently thread‐safe.
The following Animal class represents an example of an implementation of immutable object pattern:

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

public final class Animal {
private final String species;
private final int age;
private final List<String> favoriteFoods;

public Animal(String species, int age, List<String> favoriteFoods) {
this.species = species;
this.age = age;
if (favoriteFoods == null) {
throw new RuntimeException("favorite Foods is required");
}
this.favoriteFoods = new ArrayList<String>(favoriteFoods);
}

public String getSpecies() {
return species;
}

public int getAge() {
return age;
}

public int getFavoriteFoodsCount() {
return favoriteFoods.size();
}

public String getFavoriteFood(int index) {
return favoriteFoods.get(index);
}
}

Builder Pattern

The builder pattern is a creational pattern in which parameters are passed to a builder object, often through method chaining, and an object is generated with a final build call. It is often used with immutable objects, since immutable objects do not have setter methods and must be created with all of their parameters set, although it could be used with mutable objects as well.
The following AnimalBuilder class goes along with our previous class Animal and represents an example of an implementation of the builder pattern:


import java.util.List;

public class AnimalBuilder {
private String species;
private int age;
private List<String> favoriteFoods;

public AnimalBuilder setAge(int age) {
this.age = age;
return this;
}

public AnimalBuilder setSpecies(String species) {
this.species = species;
return this;
}

public AnimalBuilder setFavoriteFoods(List<String> favoriteFoods) {
this.favoriteFoods = favoriteFoods;
return this;
}

public Animal build() {
return new Animal(species, age, favoriteFoods);
}
}

Factory Pattern

The factory pattern sometimes referred to as the factory method pattern, is a creational pattern based on the idea of using a factory class to produce instances of objects based on a set of input parameters. It is similar to the builder pattern, although it is focused on supporting class polymorphism.
Less talk, here is an example:

abstract class Food {
private int quantity;

public Food(int quantity) {
this.quantity = quantity;
}

public int getQuantity() {
return quantity;
}

public abstract void consumed();
}

class Hay extends Food {
public Hay(int quantity) {
super(quantity);
}

@Override
public void consumed() {
System.out.println("Hay eaten: " + getQuantity());
}
}

class Pellets extends Food {
public Pellets(int quantity) {
super(quantity);
}

@Override
public void consumed() {
System.out.println("Pellets eaten: " + getQuantity());
}
}

public class FoodFactory {
public static Food getFood(String animalName) {
switch (animalName) {
case "zebra":
return new Hay(100);
case "rabbit":
return new Pellets(5);
case "goat":
return new Pellets(30);
}
// Good practice to throw an exception if no matching subclass could be
// found
throw new UnsupportedOperationException("Unsupported animal: " + animalName);
}
}

No comments:

Post a Comment