What is a design Pattern ?
An established solution to the most commonly encountered problems in software design.
Creational Design Pattern
Creational Design Pattern is concerned with the way in which the objects are being created.
Singleton
The Singleton Design Pattern aims to keep a check on initialization of objects of a particular class by ensuring that only one instance of the object exists throughout the Java Virtual Machine.
package com.designPattern.demo;
class DBConnection {
static DBConnection redisConnection;
int port;
String database;
private DBConnection() {
try {
Thread.sleep(2000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.port = 8010;
this.database = "Redis";
}
static DBConnection getInstance() {
if (redisConnection == null) {
redisConnection = new DBConnection();
}
return redisConnection;
}
@Override
public String toString() {
return "DBConnection{" +
"port=" + port +
", database='" + database + '\'' +
'}';
}
}
public class Singleton {
public static void main(String[] args) {
System.out.println(DBConnection.getInstance());
System.out.println(DBConnection.getInstance());
}
}
When to use singleton design pattern
package com.designPattern.demo;
interface HotDrink{
void prepareHotDrink();
}
class Tea implements HotDrink{
@Override
public void prepareHotDrink() {
System.out.println("This restaurant will serve Tea");
}
}
class Coffee implements HotDrink{
@Override
public void prepareHotDrink() {
System.out.println("This restaurant will serve coffee");
}
}
class Restaurant{
HotDrink hotDrink;
public HotDrink getHotDrink() {
return hotDrink;
}
public void setHotDrink(HotDrink hotDrink) {
this.hotDrink = hotDrink;
}
}
class RestaurantFactory{
static Restaurant getRestaurantObject(String name){
Restaurant restaurant= new Restaurant();
switch (name){
case "restaurantWithTea":
restaurant.setHotDrink(new Tea());
break;
case "restaurantWithCoffee" :
restaurant.setHotDrink(new Coffee());
break;
}
return restaurant;
}
}
public class FactoryPattern {
public static void main(String[] args) {
Restaurant restaurantWithTea = RestaurantFactory.getRestaurantObject("restaurantWithTea");
restaurantWithTea.getHotDrink().prepareHotDrink();
Restaurant restaurantWithCoffee = RestaurantFactory.getRestaurantObject("restaurantWithCoffee");
restaurantWithCoffee.getHotDrink().prepareHotDrink();
}
}
Abstract Factory
enum CarType {
MICRO, MINI, LUXURY;
}
enum Location {
DEFAULT, USA, INDIA;
}
abstract class Car {
CarType carType;
Location location;
public Car(CarType carType, Location location) {
this.carType = carType;
this.location = location;
}
abstract void construct();
@Override
public String toString() {
return "Car{" +
"carType=" + carType +
", location=" + location +
'}';
}
}
class LuxuryCar extends Car {
public LuxuryCar(Location location) {
super(CarType.LUXURY, location);
}
@Override
void construct() {
System.out.println("connecting to Luxury Car");
}
}
class MiniCar extends Car {
public MiniCar(Location location) {
super(CarType.MINI, location);
}
@Override
void construct() {
System.out.println("connecting to Mini Car");
}
}
class MicroCar extends Car {
public MicroCar(Location location) {
super(CarType.MINI, location);
}
@Override
void construct() {
System.out.println("connecting to Micro Car");
}
}
class IndianCarFactory {
static Car buildCar(CarType carType) {
Car car = null;
switch (carType) {
case MICRO:
car = new MicroCar(Location.INDIA);
break;
case MINI:
car = new MiniCar(Location.INDIA);
break;
case LUXURY:
car = new LuxuryCar(Location.INDIA);
break;
}
return car;
}
}
class DefaultCarFactory {
static Car buildCar(CarType carType) {
Car car = null;
switch (carType) {
case MICRO:
car = new MicroCar(Location.DEFAULT);
break;
case MINI:
car = new MiniCar(Location.DEFAULT);
break;
case LUXURY:
car = new LuxuryCar(Location.DEFAULT);
break;
}
return car;
}
}
class USACarFactory {
static Car buildCar(CarType carType) {
Car car = null;
switch (carType) {
case MICRO:
car = new MicroCar(Location.USA);
break;
case MINI:
car = new MiniCar(Location.USA);
break;
case LUXURY:
car = new LuxuryCar(Location.USA);
break;
}
return car;
}
}
class CarFactory {
Car car = null;
static Car buildCar(CarType carType, Location location) {
Car car = null;
switch (location) {
case INDIA:
car = IndianCarFactory.buildCar(carType);
break;
case USA:
car = USACarFactory.buildCar(carType);
break;
case DEFAULT:
car = DefaultCarFactory.buildCar(carType);
break;
}
return car;
}
}
public class AbstractFactory {
public static void main(String[] args) {
System.out.println(
CarFactory.buildCar(CarType.MICRO, Location.DEFAULT)
);
}
}
Builder
When the complexity of creating object increases, the Builder pattern can separate out the instantiation process by using another object (a builder) to construct the object.
class Employee {
private String name;
private Integer id;
private Integer salary;
private boolean optedForPF;
private boolean isOptedForNPS;
private Integer contributionToDonation;
public Employee(EmployeeBuilder employeeBuilder) {
id = employeeBuilder.getId();
name = employeeBuilder.getName();
salary = employeeBuilder.getSalary();
optedForPF = employeeBuilder.isOptedForPF();
isOptedForNPS = employeeBuilder.isOptedForNPS();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getSalary() {
return salary;
}
public void setSalary(Integer salary) {
this.salary = salary;
}
public boolean isOptedForPF() {
return optedForPF;
}
public void setOptedForPF(boolean optedForPF) {
this.optedForPF = optedForPF;
}
public boolean isOptedForNPS() {
return isOptedForNPS;
}
public void setOptedForNPS(boolean optedForNPS) {
isOptedForNPS = optedForNPS;
}
public Integer getContributionToDonation() {
return contributionToDonation;
}
public void setContributionToDonation(Integer contributionToDonation) {
this.contributionToDonation = contributionToDonation;
}
@Override
public String toString() {
return "Employee{" +
"name='" + name + '\'' +
", id=" + id +
", salary=" + salary +
", optedForPF=" + optedForPF +
", isOptedForNPS=" + isOptedForNPS +
", contributionToDonation=" + contributionToDonation +
'}';
}
}
class EmployeeBuilder {
private String name;
private Integer id;
private Integer salary;
private boolean optedForPF;
private boolean isOptedForNPS;
private Integer contributionToDonation;
public EmployeeBuilder(String name, Integer id) {
this.name = name;
this.id = id;
}
public String getName() {
return name;
}
public EmployeeBuilder setName(String name) {
this.name = name;
return this;
}
public Integer getId() {
return id;
}
public EmployeeBuilder setId(Integer id) {
this.id = id;
return this;
}
public Integer getSalary() {
return salary;
}
public EmployeeBuilder withSalary(Integer salary) {
this.salary = salary;
return this;
}
public boolean isOptedForPF() {
return optedForPF;
}
public EmployeeBuilder hasOptedForPF(boolean optedForPF) {
this.optedForPF = optedForPF;
return this;
}
public boolean isOptedForNPS() {
return isOptedForNPS;
}
public EmployeeBuilder hasOptedForNPS(boolean optedForNPS) {
isOptedForNPS = optedForNPS;
return this;
}
public Integer getContributionToDonation() {
return contributionToDonation;
}
public EmployeeBuilder willContributionAmountToDonation(Integer contributionToDonation) {
this.contributionToDonation = contributionToDonation;
return this;
}
public Employee build() {
return new Employee(this);
}
}
public class Builder {
public static void main(String[] args) {
Employee employee = new EmployeeBuilder("Pulkit",24)
.withSalary(100000)
.willContributionAmountToDonation(2000)
.hasOptedForNPS(true)
.hasOptedForPF(false)
.build();
System.out.println(employee);
}
}
Structure Design Pattern
Structural Design Pattern are concerned with how classes and Objects can be composed to form a large structure
Bridge
This means to create a bridge interface that uses OOP principles to separate out responsibilities into different abstract classes.
Elements of Bridge Design Pattern
Without Bridge Design pattern
Vehicle |
---|
Bus |
---|
Bike |
---|
ProducedBike |
---|
AssembledBike |
---|
ProducedBus |
---|
AssembledBus |
---|
With Bridge Design Pattern
Vehicle |
---|
Workshop |
---|
Bus |
---|
Bike |
---|
Produce |
---|
Assemble |
---|
// Implementor
interface Workshop {
String getWorkshopType();
}
// Concrete Implementor
class Assemble implements Workshop{
@Override
public String getWorkshopType() {
return "Assemble";
}
}
// Concrete Implementor
class Manufacture implements Workshop{
@Override
public String getWorkshopType() {
return "Manufacture";
}
}
// Abstraction
abstract class Vehicle {
String type;
Workshop workshop;
public Vehicle(String type, Workshop workshop) {
this.type = type;
this.workshop = workshop;
}
}
// Refined Abstraction
class Bike extends Vehicle {
String specification;
public Bike(String specification, Workshop workshop) {
super("2 Wheeler", workshop);
this.specification = specification;
}
@Override
public String toString() {
return "Bike{" +
"type='" + type + '\'' +
", workshop=" + workshop.getWorkshopType() +
", specification='" + specification + '\'' +
'}';
}
}
// Refined Abstraction
class Bus extends Vehicle {
Integer seats;
public Bus(Integer seats, Workshop workshop) {
super("3 Wheeler", workshop);
this.seats = seats;
}
@Override
public String toString() {
return "Bus{" +
"type='" + type + '\'' +
", workshop=" + workshop.getWorkshopType() +
", seats=" + seats +
'}';
}
}
class BridgePattern {
public static void main(String[] args) {
Bike avenger = new Bike("Cruiser", new Assemble());
Bike passion = new Bike("Regular", new Manufacture());
System.out.println(avenger);
System.out.println(passion);
Bus bus = new Bus(30, new Manufacture());
System.out.println(bus);
}
}
Decorator
interface Shape {
String info();
}
class Circle implements Shape {
private Integer radius;
public Circle(Integer radius) {
this.radius = radius;
}
public Integer getRadius() {
return radius;
}
public void setRadius(Integer radius) {
this.radius = radius;
}
@Override
public String info() {
return "A circle with radius : " + radius;
}
}
class Square implements Shape {
private Integer length;
private Integer breadth;
public Square(Integer length, Integer breadth) {
this.length = length;
this.breadth = breadth;
}
public Integer getLength() {
return length;
}
public void setLength(Integer length) {
this.length = length;
}
public Integer getBreadth() {
return breadth;
}
public void setBreadth(Integer breadth) {
this.breadth = breadth;
}
@Override
public String info() {
return "A square with length : " + length + " breadth : " + breadth;
}
}
class ColoredShape implements Shape {
private Shape shape;
private String color;
public ColoredShape(Shape shape, String color) {
this.shape = shape;
this.color = color;
}
public Shape getShape() {
return shape;
}
public void setShape(Shape shape) {
this.shape = shape;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
@Override
public String info() {
return shape.info() + " with color : " + color;
}
}
class TransparentShape implements Shape {
private Shape shape;
private Integer transparency;
public TransparentShape(Shape shape, Integer transparency) {
this.shape = shape;
this.transparency = transparency;
}
public Shape getShape() {
return shape;
}
public void setShape(Shape shape) {
this.shape = shape;
}
public Integer getTransparency() {
return transparency;
}
public void setTransparency(Integer transparency) {
this.transparency = transparency;
}
@Override
public String info() {
return shape.info() + " with transparency : " + transparency;
}
}
public class Decorator {
public static void main(String[] args) {
Square square= new Square(3,4);
System.out.println(square.info());
ColoredShape coloredShape = new ColoredShape(
new Square(5,7),
"Blue");
System.out.println(coloredShape.info());
TransparentShape transparentShape = new TransparentShape(
new ColoredShape(new Circle(5),"pink"),50);
System.out.println(transparentShape.info());
}
}
Composite
The Composite Pattern has four participants:
//Component
interface Directory {
void showDirectoryDetails();
}
//Leaf
class Developer implements Directory {
private String name;
private String favoriteLanguage;
public Developer(String name, String favoriteLanguage) {
this.name = name;
this.favoriteLanguage = favoriteLanguage;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getFavoriteLanguage() {
return favoriteLanguage;
}
public void setFavoriteLanguage(String favoriteLanguage) {
this.favoriteLanguage = favoriteLanguage;
}
@Override
public void showDirectoryDetails() {
System.out.println("Developer{" +
"name='" + name + '\'' +
", favoriteLanguage='" + favoriteLanguage + '\'' +
'}');
}
@Override
public String toString() {
return "Developer{" +
"name='" + name + '\'' +
", favoriteLanguage='" + favoriteLanguage + '\'' +
'}';
}
}
//leaf
class Manager implements Directory{
private String name;
private String type;
public Manager(String name, String type) {
this.name = name;
this.type = type;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
@Override
public void showDirectoryDetails() {
System.out.println("Manager{" +
"name='" + name + '\'' +
", type='" + type + '\'' +
'}');
}
@Override
public String toString() {
return "Manager{" +
"name='" + name + '\'' +
", type='" + type + '\'' +
'}';
}
}
//Composite
class DeveloperDirectory implements Directory{
List<Directory> developerDirectory = new ArrayList<Directory>();
@Override
public void showDirectoryDetails() {
System.out.println("Developer Directory");
developerDirectory.forEach(e->e.showDirectoryDetails());
}
}
//Composite
class ManagerDirectory implements Directory{
List<Directory> managerDirectory = new ArrayList<Directory>();
@Override
public void showDirectoryDetails() {
System.out.println("Manager Directory");
managerDirectory.forEach(e->e.showDirectoryDetails());
}
}
//Composite
class CompanyDirectory implements Directory{
List<Directory> companyDirectory = new ArrayList<>();
@Override
public void showDirectoryDetails() {
companyDirectory.forEach(e->e.showDirectoryDetails());
}
}
//Client
public class Composite {
public static void main(String[] args) {
Developer developer1 = new Developer("John","Java");
Developer developer2 = new Developer("Sam","C#");
Manager manager1 = new Manager("Devin","Associate");
Manager manager2 = new Manager("James","Senior");
DeveloperDirectory developerDirectory= new DeveloperDirectory();
developerDirectory.developerDirectory.add(developer1);
developerDirectory.developerDirectory.add(developer2);
ManagerDirectory managerDirectory = new ManagerDirectory();
managerDirectory.managerDirectory.add(manager1);
managerDirectory.managerDirectory.add(manager2);
CompanyDirectory companyDirectory = new CompanyDirectory();
companyDirectory.companyDirectory.add(developerDirectory);
companyDirectory.companyDirectory.add(managerDirectory);
companyDirectory.showDirectoryDetails();
}
}
Proxy
class Driver{
private int age;
public Driver(int age) {
this.age = age;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
interface Drivable{
void drive();
}
class Car implements Drivable{
protected Driver driver;
public Car(Driver driver) {
this.driver = driver;
}
@Override
public void drive() {
System.out.println("drive the car");
}
}
class CarProxy extends Car{
public CarProxy(Driver driver) {
super(driver);
}
@Override
public void drive() {
if(driver.getAge()<21){
System.out.println("You can't drive the car");
}else {
System.out.println("you can drive");
super.drive();
}
}
}
public class Proxy {
public static void main(String[] args) {
Driver john= new Driver(29);
Drivable car= new CarProxy(john);
car.drive();
}
}
When we want to add a layer of security to the original underlying object to provide controlled access based on access rights of the client.
Exercise