Design patterns: Difference between revisions
Line 413: | Line 413: | ||
*Concrete Builder 2 iglooBuilder.go | *Concrete Builder 2 iglooBuilder.go | ||
*Product house.go | *Product house.go | ||
[[Pattern builder2.png]] | [[File:Pattern builder2.png]] | ||
=====Code===== | =====Code===== | ||
iBuilder | iBuilder |
Latest revision as of 04:50, 21 August 2020
Creational Patterns
Abstract Factory
Description
Creates an instance of several families of classes. Provide an interface for creating families of related or dependent objects without specifying their concrete classes.
UML
Usage
Abstract Factory is a very central design pattern for Dependency Injection (DI).
AbstractFactory abstractFactory;
//creating a brown toy dog
abstractFactory = FactoryProvider.getFactory("Toy");
Animal toy = (Animal) abstractFactory.create("Dog");
abstractFactory = FactoryProvider.getFactory("Color");
Color color =(Color) abstractFactory.create("Brown");
Implementation
C#
using System;
namespace DoFactory.GangOfFour.Abstract.Structural
{
/// <summary>
/// MainApp startup class for Structural
/// Abstract Factory Design Pattern.
/// </summary>
class MainApp
{
/// <summary>
/// Entry point into console application.
/// </summary>
public static void Main()
{
// Abstract factory #1
AbstractFactory factory1 = new ConcreteFactory1();
Client client1 = new Client(factory1);
client1.Run();
// Abstract factory #2
AbstractFactory factory2 = new ConcreteFactory2();
Client client2 = new Client(factory2);
client2.Run();
// Wait for user input
Console.ReadKey();
}
}
/// <summary>
/// The 'AbstractFactory' abstract class
/// </summary>
abstract class AbstractFactory
{
public abstract AbstractProductA CreateProductA();
public abstract AbstractProductB CreateProductB();
}
/// <summary>
/// The 'ConcreteFactory1' class
/// </summary>
class ConcreteFactory1 : AbstractFactory
{
public override AbstractProductA CreateProductA()
{
return new ProductA1();
}
public override AbstractProductB CreateProductB()
{
return new ProductB1();
}
}
/// <summary>
/// The 'ConcreteFactory2' class
/// </summary>
class ConcreteFactory2 : AbstractFactory
{
public override AbstractProductA CreateProductA()
{
return new ProductA2();
}
public override AbstractProductB CreateProductB()
{
return new ProductB2();
}
}
/// <summary>
/// The 'AbstractProductA' abstract class
/// </summary>
abstract class AbstractProductA
{
}
/// <summary>
/// The 'AbstractProductB' abstract class
/// </summary>
abstract class AbstractProductB
{
public abstract void Interact(AbstractProductA a);
}
/// <summary>
/// The 'ProductA1' class
/// </summary>
class ProductA1 : AbstractProductA
{
}
/// <summary>
/// The 'ProductB1' class
/// </summary>
class ProductB1 : AbstractProductB
{
public override void Interact(AbstractProductA a)
{
Console.WriteLine(this.GetType().Name +
" interacts with " + a.GetType().Name);
}
}
/// <summary>
/// The 'ProductA2' class
/// </summary>
class ProductA2 : AbstractProductA
{
}
/// <summary>
/// The 'ProductB2' class
/// </summary>
class ProductB2 : AbstractProductB
{
public override void Interact(AbstractProductA a)
{
Console.WriteLine(this.GetType().Name +
" interacts with " + a.GetType().Name);
}
}
/// <summary>
/// The 'Client' class. Interaction environment for the products.
/// </summary>
class Client
{
private AbstractProductA _abstractProductA;
private AbstractProductB _abstractProductB;
// Constructor
public Client(AbstractFactory factory)
{
_abstractProductB = factory.CreateProductB();
_abstractProductA = factory.CreateProductA();
}
public void Run()
{
_abstractProductB.Interact(_abstractProductA);
}
}
}
Builder
Description
Separates object construction from its representation. Separate the construction of a complex object from its representation so that the same construction processes can create different representations.
UML
Usage
Builder pattern aims to “Separate the construction of a complex object from its representation so that the same construction process can create different representations.” It is used to construct a complex object step by step and the final step will return the object.
HouseBuilder iglooBuilder = new IglooHouseBuilder();
CivilEngineer engineer = new CivilEngineer(iglooBuilder);
engineer.constructHouse();
House house = engineer.getHouse();
Implementation
Java
interface HousePlan
{
public void setBasement(String basement);
public void setStructure(String structure);
public void setRoof(String roof);
public void setInterior(String interior);
}
class House implements HousePlan
{
private String basement;
private String structure;
private String roof;
private String interior;
public void setBasement(String basement)
{
this.basement = basement;
}
public void setStructure(String structure)
{
this.structure = structure;
}
public void setRoof(String roof)
{
this.roof = roof;
}
public void setInterior(String interior)
{
this.interior = interior;
}
}
interface HouseBuilder
{
public void buildBasement();
public void buildStructure();
public void bulidRoof();
public void buildInterior();
public House getHouse();
}
class IglooHouseBuilder implements HouseBuilder
{
private House house;
public IglooHouseBuilder()
{
this.house = new House();
}
public void buildBasement()
{
house.setBasement("Ice Bars");
}
public void buildStructure()
{
house.setStructure("Ice Blocks");
}
public void buildInterior()
{
house.setInterior("Ice Carvings");
}
public void bulidRoof()
{
house.setRoof("Ice Dome");
}
public House getHouse()
{
return this.house;
}
}
class TipiHouseBuilder implements HouseBuilder
{
private House house;
public TipiHouseBuilder()
{
this.house = new House();
}
public void buildBasement()
{
house.setBasement("Wooden Poles");
}
public void buildStructure()
{
house.setStructure("Wood and Ice");
}
public void buildInterior()
{
house.setInterior("Fire Wood");
}
public void bulidRoof()
{
house.setRoof("Wood, caribou and seal skins");
}
public House getHouse()
{
return this.house;
}
}
class CivilEngineer
{
private HouseBuilder houseBuilder;
public CivilEngineer(HouseBuilder houseBuilder)
{
this.houseBuilder = houseBuilder;
}
public House getHouse()
{
return this.houseBuilder.getHouse();
}
public void constructHouse()
{
this.houseBuilder.buildBasement();
this.houseBuilder.buildStructure();
this.houseBuilder.bulidRoof();
this.houseBuilder.buildInterior();
}
}
class Builder
{
public static void main(String[] args)
{
HouseBuilder iglooBuilder = new IglooHouseBuilder();
CivilEngineer engineer = new CivilEngineer(iglooBuilder);
engineer.constructHouse();
House house = engineer.getHouse();
System.out.println("Builder constructed: "+ house);
}
}
Go
Introduction
- Director director.go
- Builder Interface iBuilder.go
- Concrete Builder 1 normalBuilder.go
- Concrete Builder 2 iglooBuilder.go
- Product house.go
Code
iBuilder
package main
type iBuilder interface {
setWindowType()
setDoorType()
setNumFloor()
getHouse() house
}
func getBuilder(builderType string) iBuilder {
if builderType == "normal" {
return &normalBuilder{}
}
if builderType == "igloo" {
return &iglooBuilder{}
}
return nil
}
normalBuilder
package main
type normalBuilder struct {
windowType string
doorType string
floor int
}
func newNormalBuilder() *normalBuilder {
return &normalBuilder{}
}
func (b *normalBuilder) setWindowType() {
b.windowType = "Wooden Window"
}
func (b *normalBuilder) setDoorType() {
b.doorType = "Wooden Door"
}
func (b *normalBuilder) setNumFloor() {
b.floor = 2
}
func (b *normalBuilder) getHouse() house {
return house{
doorType: b.doorType,
windowType: b.windowType,
floor: b.floor,
}
}
IglooBuilder
package main
type iglooBuilder struct {
windowType string
doorType string
floor int
}
func newIglooBuilder() *iglooBuilder {
return &iglooBuilder{}
}
func (b *iglooBuilder) setWindowType() {
b.windowType = "Snow Window"
}
func (b *iglooBuilder) setDoorType() {
b.doorType = "Snow Door"
}
func (b *iglooBuilder) setNumFloor() {
b.floor = 1
}
func (b *iglooBuilder) getHouse() house {
return house{
doorType: b.doorType,
windowType: b.windowType,
floor: b.floor,
}
}
House
package main
type house struct {
windowType string
doorType string
floor int
}
Director
package main
type director struct {
builder iBuilder
}
func newDirector(b iBuilder) *director {
return &director{
builder: b,
}
}
func (d *director) setBuilder(b iBuilder) {
d.builder = b
}
func (d *director) buildHouse() house {
d.builder.setDoorType()
d.builder.setWindowType()
d.builder.setNumFloor()
return d.builder.getHouse()
}
main
package main
import "fmt"
func main() {
normalBuilder := getBuilder("normal")
iglooBuilder := getBuilder("igloo")
director := newDirector(normalBuilder)
normalHouse := director.buildHouse()
fmt.Printf("Normal House Door Type: %s\n", normalHouse.doorType)
fmt.Printf("Normal House Window Type: %s\n", normalHouse.windowType)
fmt.Printf("Normal House Num Floor: %d\n", normalHouse.floor)
director.setBuilder(iglooBuilder)
iglooHouse := director.buildHouse()
fmt.Printf("\nIgloo House Door Type: %s\n", iglooHouse.doorType)
fmt.Printf("Igloo House Window Type: %s\n", iglooHouse.windowType)
fmt.Printf("Igloo House Num Floor: %d\n", iglooHouse.floor)
}
Factory Method
Description
Creates an instance of several derived classes. Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.
UML
Usage
In Factory pattern, we create object without exposing the creation logic to the client and refer to newly created object using a common interface.
VehicleFactory factory = new ConcreteVehicleFactory();
IFactory scooter = factory.GetVehicle("Scooter");
scooter.Drive(10);
IFactory bike = factory.GetVehicle("Bike");
Implementation
C#
using System;
namespace Factory
{
/// <summary>
/// The 'Product' interface
/// </summary>
public interface IFactory
{
void Drive(int miles);
}
/// <summary>
/// A 'ConcreteProduct' class
/// </summary>
public class Scooter : IFactory
{
public void Drive(int miles)
{
Console.WriteLine("Drive the Scooter : " + miles.ToString() + "km");
}
}
/// <summary>
/// A 'ConcreteProduct' class
/// </summary>
public class Bike : IFactory
{
public void Drive(int miles)
{
Console.WriteLine("Drive the Bike : " + miles.ToString() + "km");
}
}
/// <summary>
/// The Creator Abstract Class
/// </summary>
public abstract class VehicleFactory
{
public abstract IFactory GetVehicle(string Vehicle);
}
/// <summary>
/// A 'ConcreteCreator' class
/// </summary>
public class ConcreteVehicleFactory : VehicleFactory
{
public override IFactory GetVehicle(string Vehicle)
{
switch (Vehicle)
{
case "Scooter":
return new Scooter();
case "Bike":
return new Bike();
default:
throw new ApplicationException(string.Format("Vehicle '{0}' cannot be created", Vehicle));
}
}
}
Prototype
Description
A fully initialized instance to be copied or cloned. Specify the kinds of objects to create using a prototypical instance, and create new objects by copying this prototype.
UML
Usage
This pattern provides a way to create types which could be expensive to create in real time. The cache creates a hash of possible types which can be created and getShape provides method to get a new object.
ShapeCache.loadCache();
Shape clonedShape1 = (Shape) ShapeCache.getShape("1");
Shape clonedShape2 = (Shape) ShapeCache.getShape("2");
Shape clonedShape3 = (Shape) ShapeCache.getShape("3");
Implementation
Java
Base Shape
public abstract class Shape implements Cloneable {
private String id;
protected String type;
abstract void draw();
public String getType(){
return type;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Object clone() {
Object clone = null;
try {
clone = super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return clone;
}
}
Concrete class Rectangle
public class Rectangle extends Shape {
public Rectangle(){
type = "Rectangle";
}
@Override
public void draw() {
System.out.println("Inside Rectangle::draw() method.");
}
}
Concrete class Square
public class Square extends Shape {
public Square(){
type = "Square";
}
@Override
public void draw() {
System.out.println("Inside Square::draw() method.");
}
}
Create a class to get concrete classes from database and store them in a Hashtable.
import java.util.Hashtable;
public class ShapeCache {
private static Hashtable<String, Shape> shapeMap = new Hashtable<String, Shape>();
public static Shape getShape(String shapeId) {
Shape cachedShape = shapeMap.get(shapeId);
return (Shape) cachedShape.clone();
}
// for each shape run database query and create shape
// shapeMap.put(shapeKey, shape);
// for example, we are adding three shapes
public static void loadCache() {
Circle circle = new Circle();
circle.setId("1");
shapeMap.put(circle.getId(),circle);
Square square = new Square();
square.setId("2");
shapeMap.put(square.getId(),square);
Rectangle rectangle = new Rectangle();
rectangle.setId("3");
shapeMap.put(rectangle.getId(), rectangle);
}
}
Singleton
Description
A class of which only a single instance can exist. Ensure a class only has one instance, and provide a global point of access to it.
UML
Usage
It is used to provide global point of access to the object. In terms of practical use Singleton patterns are used in logging, caches, thread pools, configuration settings, device driver objects. Design pattern is often used in conjunction with Factory design pattern.
Implementation
C#
public sealed class Singleton
{
Singleton()
{
}
private static readonly object padlock = new object();
private static Singleton instance = null;
public static Singleton Instance
{
get
{
lock (padlock)
{
if (instance == null)
{
instance = new Singleton();
}
return instance;
}
}
}
}
Go
package singleton
import (
"sync"
)
type singleton struct {}
var instance *singleton
var once sync.Once
func GetInstance() *singleton {
once.Do(func() {
instance = &singleton{}
})
return instance
}
Java
// Java program implementing Singleton class // with getInstance() method
class Singleton
{
// static variable single_instance of type Singleton
private static Singleton single_instance = null;
// variable of type String
public String s;
// private constructor restricted to this class itself
private Singleton()
{
s = "Hello I am a string part of Singleton class";
}
// static method to create instance of Singleton class
public static Singleton getInstance()
{
if (single_instance == null)
single_instance = new Singleton();
return single_instance;
}
}
Python
class Singleton:
__instance = None
@staticmethod
def getInstance():
""" Static access method. """
if Singleton.__instance == None:
Singleton()
return Singleton.__instance
def __init__(self):
""" Virtually private constructor. """
if Singleton.__instance != None:
raise Exception("This class is a singleton!")
else:
Singleton.__instance = self
s = Singleton()
print s
s = Singleton.getInstance()
print s
s = Singleton.getInstance()
print s
Rust
use ruspiro_singleton::*;
static FOO: Singleton<Foo> = Singleton::new(Foo::new(0));
struct Foo {
count: u32,
}
impl Foo {
pub const fn new(initial_count: u32) -> Self {
Foo {
count: initial_count,
}
}
pub fn count(&self) -> u32 {
self.count
}
pub fn add_count(&mut self, value: u32) -> u32 {
self.count += value;
self.count
}
}
Structural Patterns
Adapter
Description
Match interfaces of different classes.Convert the interface of a class into another interface clients expect. Adapter lets classes work together that couldn’t otherwise because of incompatible interfaces.
UML
Usage
This can be used on GUIs to allow an object to be displayed, for instance, in a grid control
Implementation
C#
using System;
namespace DoFactory.GangOfFour.Adapter.Structural
{
/// <summary>
/// MainApp startup class for Structural
/// Adapter Design Pattern.
/// </summary>
class MainApp
{
/// <summary>
/// Entry point into console application.
/// </summary>
static void Main()
{
// Create adapter and place a request
Target target = new Adapter();
target.Request();
// Wait for user
Console.ReadKey();
}
}
/// <summary>
/// The 'Target' class
/// </summary>
class Target
{
public virtual void Request()
{
Console.WriteLine("Called Target Request()");
}
}
/// <summary>
/// The 'Adapter' class
/// </summary>
class Adapter : Target
{
private Adaptee _adaptee = new Adaptee();
public override void Request()
{
// Possibly do some other work
// and then call SpecificRequest
_adaptee.SpecificRequest();
}
}
/// <summary>
/// The 'Adaptee' class
/// </summary>
class Adaptee
{
public void SpecificRequest()
{
Console.WriteLine("Called SpecificRequest()");
}
}
}
Java
This example adapts (converts) the Vlc and Mp4 media to a common interface
public interface MediaPlayer {
public void play(String audioType, String fileName);
}
public interface AdvancedMediaPlayer {
public void playVlc(String fileName);
public void playMp4(String fileName);
}
public class VlcPlayer implements AdvancedMediaPlayer{
@Override
public void playVlc(String fileName) {
System.out.println("Playing vlc file. Name: "+ fileName);
}
@Override
public void playMp4(String fileName) {
//do nothing
}
}
public class Mp4Player implements AdvancedMediaPlayer{
@Override
public void playVlc(String fileName) {
//do nothing
}
@Override
public void playMp4(String fileName) {
System.out.println("Playing mp4 file. Name: "+ fileName);
}
}
Here is the adapter class which converts the two players to a known interface
public class MediaAdapter implements MediaPlayer {
AdvancedMediaPlayer advancedMusicPlayer;
public MediaAdapter(String audioType){
if(audioType.equalsIgnoreCase("vlc") ){
advancedMusicPlayer = new VlcPlayer();
}else if (audioType.equalsIgnoreCase("mp4")){
advancedMusicPlayer = new Mp4Player();
}
}
@Override
public void play(String audioType, String fileName) {
if(audioType.equalsIgnoreCase("vlc")){
advancedMusicPlayer.playVlc(fileName);
}
else if(audioType.equalsIgnoreCase("mp4")){
advancedMusicPlayer.playMp4(fileName);
}
}
}
Here is the player which uses the adapter
public class AudioPlayer implements MediaPlayer {
MediaAdapter mediaAdapter;
@Override
public void play(String audioType, String fileName) {
//inbuilt support to play mp3 music files
if(audioType.equalsIgnoreCase("mp3")){
System.out.println("Playing mp3 file. Name: " + fileName);
}
//mediaAdapter is providing support to play other file formats
else if(audioType.equalsIgnoreCase("vlc") || audioType.equalsIgnoreCase("mp4")){
mediaAdapter = new MediaAdapter(audioType);
mediaAdapter.play(audioType, fileName);
}
else{
System.out.println("Invalid media. " + audioType + " format not supported");
}
}
}
Here is the demo
public class AdapterPatternDemo {
public static void main(String[] args) {
AudioPlayer audioPlayer = new AudioPlayer();
audioPlayer.play("mp3", "beyond the horizon.mp3");
audioPlayer.play("mp4", "alone.mp4");
audioPlayer.play("vlc", "far far away.vlc");
audioPlayer.play("avi", "mind me.avi");
}
}
Bridge
Description
Separates an object’s interface from its implementation. Decouple an abstraction from its implementation so that the two can vary independently.
UML
Composite
Description
A tree structure of simple and composite objects. Compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly.
UML
Decorator
Description
Add responsibilities to objects dynamically. Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.
UML
Facade
Description
A single class that represents an entire subsystem. Provide a unified interface to a set of interfaces in a system. Facade defines a higher-level interface that makes the subsystem easier to use.
UML
Flyweight
Description
A fine-grained instance used for efficient sharing. Use sharing to support large numbers of fine-grained objects efficiently. A flyweight is a shared object that can be used in multiple contexts simultaneously. The flyweight acts as an independent object in each context — it’s indistinguishable from an instance of the object that’s not shared.
UML
Proxy
Description
An object representing another object. Provide a surrogate or placeholder for another object to control access to it.
UML
Behavioural Patterns
Chain of Responsibility
Description
A way of passing a request between a chain of objects. Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it.
UML
Command
Description
Encapsulate a command request as an object. Encapsulate a request as an object, thereby letting you parameterise clients with different requests, queue or log requests, and support undoable operations.
UML
Interpreter
Description
A way to include language elements in a program. Given a language, define a representation for its grammar along with an interpreter that uses the representation to interpret sentences in the language.
UML
Iterator
Description
Sequentially access the elements of a collection. Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation.
UML
Mediator
Description
Defines simplified communication between classes. Define an object that encapsulates how a set of objects interact. Mediator promotes loose coupling by keeping objects from referring to each other explicitly, and it lets you vary their interaction independently.
UML
Memento
Description
Capture and restore an object's internal state. Without violating encapsulation, capture and externalize an object’s internal state so that the object can be restored to this state later.
UML
Observer
Description
A way of notifying change to a number of classes. Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.
UML
State
Description
Alter an object's behaviour when its state changes. Allow an object to alter its behavior when its internal state changes. The object will appear to change its class.
UML
Strategy
Description
Encapsulates an algorithm inside a class. Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it.
UML
Template
Description
Defer the exact steps of an algorithm to a subclass. Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm’s structure.
UML
Visitor
Description
Defines a new operation to a class without change. Represent an operation to be performed on the elements of an object structure. Visitor lets you define a new operation without changing the classes of the elements on which it operates.