What is it?
Design patterns are typical solutions to common problems in software design. Each pattern is like a blueprint that you can customize to solve a particular design problem in your code.
The pattern is not a specific piece of code, but a general concept for solving a particular problem. You can follow the pattern details and implement a solution that suits the realities of your own program.
Patterns are often confused with algorithms, because both concepts describe typical solutions to some known problems. While an algorithm always defines a clear set of actions that can achieve some goal, a pattern is a more high-level description of a solution. The code of the same pattern applied to two different programs may be different.
Creational
Creational patterns provide various object creation mechanisms, which increase flexibility and reuse of existing code.
Factory
- Simple Factory Pattern
- Abstract Factory Pattern
- Factory Method Pattern
Simple Factory Pattern
The Simple Factory Pattern defines a technique to encapsulate the details of object creation in a single class known as a “Factory” class whose job it is to create concrete instances of various objects that implement a given interface. The other objects in the application that need new objects created by the factory class are known as “clients” of the factory. By doing this, we ensure the following:
- Object creation code appears in one place only (rather than multiple times as in our previous example)
- Changes to object creation code are limited to the factory class and do not affect the factory’s clients – changes are made in one place at one time.
- Clients of the factory class need not (and will not) know the actual concrete class type of the objects that are created for it – therefore they are not coupled to any concrete classes.
- The client classes are closed for modification
public interface Aircraft {
void load();
void takeoff();
String getType();
}
/*
* Factory Pattern
*/
public class AircraftFactory {
// Declare a private constructor to prevent class instances from being created in any other places
private AircraftFactory() {}
// use a static method to get object with the given type
public static Aircraft createAircraft(String type) {
if (type.equals("TroopTransportCraft")) {
return new TroopTransportCraft();
} else if (type.equals("FighterCraft")) {
return new FighterCraft();
} else {
return null;
}
}
}
public class AircraftProcessor {
@Deprecated
public void process(String type) {
Aircraft acft = null;
if (type.equals("PassengerCraft")) {
acft = new PassengerCraft();
} else if (type.equals("BomberCraft")) {
acft = new BomberCraft();
}
acft.load();
acft.takeoff();
}
public void processNew(String type) {
Aircraft acft = AircraftFactory.createAircraft(type);
acft.load();
acft.takeoff();
}
}
Abstract Factory Pattern
Create interface for factory, then create different factories with implementing the factory interface.
public interface AircraftFactory {
public Aircraft createAircraft(String type);
}
public class USAircraftFactory implements AircraftFactory {
public Aircraft createAircraft(String type) {
if (type.equals("Bomber")) {
return new USBomberCraft();
} else if (type.equals("Fighter")) {
return new USFighterCraft();
} else {
return null;
}
}
}
public class BritishAircraftFactory implements AircraftFactory {
public Aircraft createAircraft(String type) {
if (type.equals("Bomber")) {
return new BritishBomberCraft();
} else if (type.equals("Fighter")) {
return new BritishFighterCraft();
} else {
return null;
}
}
}
Caller:
public class AircraftProcessor {
private AircraftFactory aircraftFactory = null;
public AircraftProcessor(AircraftFactory af) {
aircraftFactory = af;
}
public void process(String type) {
Aircraft acft = aircraftFactory.createAircraft(type);
acft.load();
acft.takeoff();
}
}
Factory Method Pattern
Create abstract class for factory, then create different factories with extending the base factory class.
public abstract class AircraftFactory {
public abstract Aircraft createAircraft(String type);
}
public class USAircraftFactory extends AircraftFactory {
public Aircraft createAircraft(String type) {
if (type.equals("Bomber")) {
return new USBomberCraft();
} else if (type.equals("Fighter")) {
return new USFighterCraft();
} else {
return null;
}
}
}
public class BritishAircraftFactory extends AircraftFactory {
public Aircraft createAircraft(String type) {
if (type.equals("Bomber")) {
return new BritishBomberCraft();
} else if (type.equals("Fighter")) {
return new BritishFighterCraft();
} else {
return null;
}
}
}
Caller:
public class AircraftProcessor {
private AircraftFactory aircraftFactory = null;
public AircraftProcessor(AircraftFactory af) {
aircraftFactory = af;
}
public void process(String type) {
Aircraft acft = aircraftFactory.createAircraft(type);
acft.load();
acft.takeoff();
}
}
Builder
Builder is a creational design pattern that lets you construct complex objects step by step. The pattern allows you to produce different types and representations of an object using the same construction code.
/* "Product" */
class Pizza {
private String dough = "";
private String sauce = "";
private String topping = "";
public void setDough(String dough) {
this.dough = dough;
}
public void setSauce(String sauce) {
this.sauce = sauce;
}
public void setTopping(String topping) {
this.topping = topping;
}
}
/* "Abstract Builder" */
abstract class PizzaBuilder {
protected Pizza pizza;
public Pizza getPizza() {
return pizza;
}
public void createNewPizzaProduct() {
pizza = new Pizza();
}
public abstract void buildDough();
public abstract void buildSauce();
public abstract void buildTopping();
}
/* "ConcreteBuilder" */
class HawaiianPizzaBuilder extends PizzaBuilder {
public void buildDough() {
pizza.setDough("cross");
}
public void buildSauce() {
pizza.setSauce("mild");
}
public void buildTopping() {
pizza.setTopping("ham+pineapple");
}
}
/* "ConcreteBuilder" */
class SpicyPizzaBuilder extends PizzaBuilder {
public void buildDough() {
pizza.setDough("pan baked");
}
public void buildSauce() {
pizza.setSauce("hot");
}
public void buildTopping() {
pizza.setTopping("pepperoni+salami");
}
}
/* "Director" */
class Waiter {
private PizzaBuilder pizzaBuilder;
public void setPizzaBuilder(PizzaBuilder pb) {
pizzaBuilder = pb;
}
public Pizza getPizza() {
return pizzaBuilder.getPizza();
}
public void constructPizza() {
pizzaBuilder.createNewPizzaProduct();
pizzaBuilder.buildDough();
pizzaBuilder.buildSauce();
pizzaBuilder.buildTopping();
}
}
/* A customer ordering a pizza. */
public class PizzaBuilderDemo {
public static void main(String[] args) {
Waiter waiter = new Waiter();
PizzaBuilder hawaiianPizzabuilder = new HawaiianPizzaBuilder();
PizzaBuilder spicyPizzaBuilder = new SpicyPizzaBuilder();
waiter.setPizzaBuilder( hawaiianPizzabuilder );
waiter.constructPizza();
Pizza pizza = waiter.getPizza();
}
}
Prototype
Prototype is a creational design pattern that Specify the kinds of objects to create using a prototypical instance, and create new objects by copying this prototype.
interface Person {
Person clone();
}
class Tom implements Person {
private final String NAME = "Tom";
@Override
public Person clone() {
return new Tom();
}
@Override
public String toString() {
return NAME;
}
}
class Dick implements Person {
private final String NAME = "Dick";
@Override
public Person clone() {
return new Dick();
}
@Override
public String toString() {
return NAME;
}
}
class Factory {
private static final Map<String, Person> prototypes = new HashMap<>();
static {
prototypes.put("tom", new Tom());
prototypes.put("dick", new Dick());
}
public static Person getPrototype(String type) {
try {
return prototypes.get(type).clone();
} catch (NullPointerException ex) {
System.out.println("Prototype with name: " + type + ", doesn't exist");
return null;
}
}
}
public class PrototypeFactory {
public static void main(String[] args) {
if (args.length > 0) {
for (String type : args) {
Person prototype = Factory.getPrototype(type);
if (prototype != null) {
System.out.println(prototype);
}
}
} else {
System.out.println("Run again with arguments of command string ");
}
}
}
Singleton
Singleton is a creational design pattern that lets you ensure that a class has only one instance, while providing a global access point to this instance.
public final class Singleton {
private static volatile Singleton instance;
private Singleton() {}
public static Singleton getInstance(String value) {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
- Declares class “Singleton” as final, so that subclasses cannot be created that could provide multiple instantiations.
- Declare a private constructor – only the Singleton class itself can instantiate a Singleton object using this constructor.
- Declares a static reference to a Singleton object and invokes the private constructor
// Define class as final, so it can't be inherited
public final class Singleton {
private volatile static Singleton instance;
// Declare a private constructor to prevent class instances from being created in any other places
private Singleton() {}
// Use a static method to get object of this class
public static synchronized Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) { // Double-Check!
instance = new Singleton();
}
}
}
return instance;
}
}
-
For variables marked with the “volatile” keyword, threads will be required to access the value of “ourInstance” from main memory, rather than access cached variable values in local (thread) memory.
-
Double check to see if instance is null or not