⬜ 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 [creational] ⇑
Abstract factory allows you to create related objects without specifying them concrete classes.
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 allows you to adapt an object with incompatible interface to another one.
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 allows you to divide complicated class into its abstraction and implementation.
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 allows you to create objects in stages.
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 [behavioral] ⇑
Chain of responsibility allows you to handle a request based on a defined process.
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 allows you to parameterize objects using requests and execute them in a specific order.
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 allows you to treat distinct entities like nested objects.
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 allows you to add a new functionality to existing classes by wrapping the original 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 allows you to separate out external logic from complex logic.
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.
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 [creational] ⇑
Fluent interface allows you to create and edit objects using method chaining.
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 allows you to define a class responsible for communication between components.
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 allows you to define a class responsible for subscription mechanism.
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 allows you to add a new functionality to existing class and control access to it.
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 allows you to have only one instance of a class and provide global access to it.
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.
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 allows you to change object behavior when its internal state changes.
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 allows you to define a family of algorithms encapsulating them in the form of separate classes.
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.
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"