plkpiotr / design-patterns

Design patterns with real-life examples and PlantUML diagrams [2021]

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

⬜ Creational 🟨 Structural πŸŸ₯ Behavioral Assumption:
Abstract factory Adapter Chain of responsibility Client code with output for each pattern
Builder Bridge Command console functions only in the client code
Factory method Composite Mediator PlantUML diagram for each pattern
Fluent interface Decorator Observer Classes used in the client code marked in UML
Singleton Facade Specification Classes/interfaces from one pattern in one folder
Proxy State Almost all classes/interfaces in separate files
Strategy Angular as ecosystem (easy to launch and test)
Template method All patterns presented in AppComponent

Abstract factory allows you to create related objects without specifying them concrete classes.

UML diagram:

abstract-factory

Client code:

const retroFactory = new RetroFactory();
const retroChair = retroFactory.createChair();
const retroTable = retroFactory.createTable();
console.log(retroTable.showRegularOffer()); // "retro table itself costs $499"
console.log(retroTable.showSpecialOffer(retroChair)); // "retro table with any chair cost $599, retro chair itself costs $199"

const modernFactory = new ModernFactory();
const modernTable = modernFactory.createTable();
console.log(modernTable.showRegularOffer()); // "modern table itself costs $399"
console.log(modernTable.showSpecialOffer(retroChair)); // "modern table with any chair cost $499, retro chair itself costs $199"

Adapter [structural] ⇑

Adapter allows you to adapt an object with incompatible interface to another one.

UML diagram:

adapter

Client code:

const movie = new Movie();
console.warn(movie.displayVGA()); // [640, 480]

const remake = new Remake();
console.warn(remake.displayHD()); // [1920, 1080]

const adaptedRemake = new Adapter(remake);
console.warn(adaptedRemake.displayVGA()); // [640, 360]

Bridge [structural] ⇑

Bridge allows you to divide complicated class into its abstraction and implementation.

UML diagram:

bridge

Client code:

const flatScreen = new FlatScreen();
let embeddedControl = new EmbeddedControl(flatScreen);
let remoteControl = new RemoteControl(flatScreen);
console.warn(embeddedControl.turnVolumeUp()); // "red light blinked, turned the volume up"
console.warn(remoteControl.turnVolumeUp()); // "red light blinked, turned the volume up"
console.warn(remoteControl.addChannelToFavorites()); // "red light blinked, added channel to favorites"

const decoder = new Decoder();
embeddedControl = new EmbeddedControl(decoder);
remoteControl = new RemoteControl(decoder);
console.warn(embeddedControl.turnVolumeUp()); // "green light blinked, turned the volume up"
console.warn(remoteControl.turnVolumeUp()); // "green light blinked, turned the volume up"
console.warn(remoteControl.addChannelToFavorites()); // "green light blinked, added channel to favorites"

Builder [creational] ⇑

Builder allows you to create objects in stages.

UML diagram:

builder

Client code:

const manager = new Manager();
const designer = new Designer();
manager.setBuilder(designer);

manager.manageBasicVersion();
const basicCar = designer.putCarIntoUse();
console.log(basicCar); // Car {engine: "1.5 VVT-i", price: 59000}

manager.managePremiumVersion();
const premiumCar = designer.putCarIntoUse();
console.log(premiumCar); // Car {engine: "1.8 D-4D", price: 72000}

designer.withPrice(63000);
designer.withEngine('1.6 D-4D');
const customCar = designer.putCarIntoUse();
console.log(customCar); // Car {price: 63000, engine: "1.6 D-4D"}

Chain of responsibility allows you to handle a request based on a defined process.

UML diagram:

chain-of-responsibility

Client code:

const cashier = new Cashier();
const securityGuard = new SecurityGuard();
const waitress = new Waitress();
cashier.addNextHandler(securityGuard)
  .addNextHandler(waitress);
console.error(cashier.handle('sell a ticket')); // "cashier sold a ticket"
console.error(cashier.handle('prepare food')); // "waitress prepared food"
console.error(cashier.handle('get a haircut')); // "nobody was able to do that"
console.error(securityGuard.handle('sell a ticket')); // "nobody was able to do that"

Command [behavioral] ⇑

Command allows you to parameterize objects using requests and execute them in a specific order.

UML diagram:

command

Client code:

const customer = new Customer();
const cashMachine = new CashMachine(1000);
customer.setFirstCommand(cashMachine);
const bankEmployee = new BankEmployee();
const bank = new Bank(bankEmployee, 'mortgage');
customer.setSecondCommand(bank);
const commandsStepByStep = customer.executeCommandsStepByStep();
console.error(commandsStepByStep); // "cash out (1000), sign a contract (mortgage)"

Composite [behavioral] ⇑

Composite allows you to treat distinct entities like nested objects.

UML diagram:

composite

Client code:

const ceo = new Employee();
console.warn(ceo.showSalary()); // 3000

const department = new Department();
department.addEntity(ceo);
const manager = new Employee();
department.addEntity(manager);
console.warn(department.showSalary()); // 6000

const section = new Department();
department.addEntity(section);
const worker = new Employee();
section.addEntity(worker);
console.warn(section.showSalary()); // 3000
console.warn(department.showSalary()); // 9000

Decorator [structural] ⇑

Decorator allows you to add a new functionality to existing classes by wrapping the original code.

UML diagram:

decorator

Client code:

const woman = new Woman();
const withCasualClothes = woman.wear();
console.warn(withCasualClothes); // "worn casual clothes"

const clothingStore = new ClothingStore(woman);
const withScarf = clothingStore.wear();
console.warn(withScarf); // "worn casual clothes, scarf"

const jeweller = new Jeweller(clothingStore);
const withBracelet = jeweller.wear();
console.warn(withBracelet); // "worn casual clothes, scarf, bracelet"

Facade [structural] ⇑

Facade allows you to separate out external logic from complex logic.

UML diagram:

facade

Client code:

const firstWaiter = new Waiter();
console.warn(firstWaiter.fillOrder()); // "dinner, coffee"

const cook = new Cook();
const barista = new Barista();
const secondWaiter = new Waiter(cook, barista);
console.warn(secondWaiter.fillDoubleOrder()); // "dinner x2, coffee x2"

Factory method [creational] ⇑

Factory method allows you to create objects using subclasses that can change the type of created object.

UML diagram:

factory-method

Client code:

const painterStudio = new PainterStudio();
const painting = painterStudio.createMasterpiece();
console.log(painting); // "created a painting"

const sculptorStudio = new SculptorStudio();
const sculpture = sculptorStudio.createMasterpiece();
console.log(sculpture); // "created a sculpture"

Fluent interface allows you to create and edit objects using method chaining.

UML diagram:

fluent-interface

Client code:

const album = new Label().withName('Recovery')
  .withTracks(['Not Afraid', 'On Fire'])
  .release();

const deluxeAlbum = new Label(album).withTracks(['Not Afraid', 'On Fire', 'So Bad'])
  .release();

console.log(album); // Album { name: "Recovery", tracks: ["Not Afraid", "On Fire"] }
console.log(deluxeAlbum); // Album { name: "Recovery", tracks: ["Not Afraid", "On Fire", "So Bad"] }

Mediator [behavioral] ⇑

Mediator allows you to define a class responsible for communication between components.

UML diagram:

mediator

Client code:

const ambulance = new Ambulance();
console.error(ambulance.notifyUnderControl()); // "ambulance is not assign to any dispatch"

const helicopter = new Helicopter();
const dispatch = new Dispatch(ambulance, helicopter);
console.error(ambulance.notifyUnderControl()); // "helicopter is not needed"
console.error(helicopter.notifyForBackup()); // "ambulance arrives, helicopter is busy"

const substituteAmbulance = new Ambulance(dispatch);
console.error(substituteAmbulance.notifyUnderControl()); // "helicopter is not needed"

Observer [behavioral] ⇑

Observer allows you to define a class responsible for subscription mechanism.

UML diagram:

observer

Client code:

const book = new Book();
const collector = new Collector();
console.error(book.follow(collector)); // "follower started following book"

const novice = new Novice();
console.error(book.follow(novice)); // "follower started following book"
console.error(book.changePrice(69)); // "collector received notification"
console.error(book.changePrice(49)); // "collector received notification, novice received notification"
console.error(book.unfollow(novice)); // "follower stopped following book"
console.error(book.changePrice(39)); // "collector received notification"

Proxy [structural] ⇑

Proxy allows you to add a new functionality to existing class and control access to it.

UML diagram:

proxy

Client code:

const patient = new Patient();
console.warn(patient.visitHospital()); // "hospital visited"

const pandemicPatient = new PandemicPatient(patient);
console.warn(pandemicPatient.visitHospital()); // "hands disinfected (access granted), hospital visited"

Singleton [creational] ⇑

Singleton allows you to have only one instance of a class and provide global access to it.

UML diagram:

singleton

Client code:

const king = King.getInstance();
const sameKing = King.getInstance();

console.log(king === sameKing); // true
console.log(king.showKingName()); // "Louis XX"
console.log(sameKing.showKingName()); // "Louis XX"

Specification [behavioral] ⇑

Specification allows you to combine business logic with boolean logic.

UML diagram:

specification

Client code:

const firstSpecification = new GreaterThan(2);
console.error(firstSpecification.isSatisfiedBy(3)); // true
console.error(firstSpecification.isSatisfiedBy(5)); // true

const secondSpecification = new LessThan(4);
const thirdSpecification = firstSpecification.and(secondSpecification);
console.error(thirdSpecification.isSatisfiedBy(3)); // true
console.error(thirdSpecification.isSatisfiedBy(5)); // false

const fourthSpecification = thirdSpecification.not();
console.error(fourthSpecification.isSatisfiedBy(3)); // false
console.error(fourthSpecification.isSatisfiedBy(5)); // true

State [behavioral] ⇑

State allows you to change object behavior when its internal state changes.

UML diagram:

state

Client code:

const liquidState = new LiquidState();
const hydrogenOxide = new HydrogenOxide(liquidState);
console.error(hydrogenOxide.warm()); // "still water"
console.error(hydrogenOxide.cool()); // "ice"
console.error(hydrogenOxide.showName()); // "H2O"

const solidState = new SolidState();
hydrogenOxide.changeState(solidState);
console.error(hydrogenOxide.warm()); // "water"
console.error(hydrogenOxide.cool()); // "ice"
console.error(hydrogenOxide.showName()); // "H2O"

Strategy [behavioral] ⇑

Strategy allows you to define a family of algorithms encapsulating them in the form of separate classes.

UML diagram:

strategy

Client code:

const defensiveStrategy = new DefensiveStrategy();
const team = new Team(defensiveStrategy);
const defensiveLineup = team.prepareLineup();
console.error(defensiveLineup); // ["Pavard", "Lewandowski"]

const offensiveStrategy = new OffensiveStrategy();
team.setStrategy(offensiveStrategy);
const offensiveLineup = team.prepareLineup();
console.error(offensiveLineup); // ["Kimmich", "Lewandowski"]

Template method [behavioral] ⇑

Template method allows you to define skeleton of an algorithm and further specify it in subclasses.

UML diagram:

template-method

Client code:

const hawaiianPizza = new HawaiianPizza();
console.error(hawaiianPizza.templateMethod()); // "dough, sauce, cheddar, ham, pineapple"

const mexicanPizza = new MexicanPizza();
console.error(mexicanPizza.templateMethod()); // "dough, sauce, mozzarella, beef, jalapenos, hot ketchup"

About

Design patterns with real-life examples and PlantUML diagrams [2021]

License:MIT License


Languages

Language:TypeScript 94.9%Language:JavaScript 4.0%Language:CSS 0.6%Language:HTML 0.5%