Patterns & fuctional programming intro

Patterns

Patterns

Паттерн проектирования — это часто встречающееся решение определённой проблемы при проектировании архитектуры программ.

Классификация

Классификация паттернов

Порождающие паттерны

Порождающие паттерны

Фабричный метод

            function Bob() {
                this.prefix = '';
                this.say = function() {
                    console.log(`${this.prefix}, i'm Bob`);
                }
            }
            const person = new Bob();
            person.prefix = 'Hello';
            person.say();
        

Фабричный метод

            function Piter() {
                this.prefix = '';
                this.say = function () {
                    console.log(`${this.prefix}, i'm Piter`);
                }
            }
            const person = new Piter();
            person.prefix = 'Hello';
            person.say();
        

Фабричный метод

            function Factory() {
                this.create = function(type) {
                    this.person = null;
                    switch (type) {
                        case 'Bob':
                            this.person = new Bob();
                            break;
                        case 'Piter':
                            this.person = new Piter();
                            break;
                        default:
                            return this.person;
                     }
                     this.person.prefix = 'Hello';
                     return this.person;
                };
            }
        

Фабричный метод

            const person = new Factory();
            const bob = person.create('Bob');
            bob.say();
            const piter = person.create('Piter');
            piter.say();
        

Абстрактная фабрика

            function Person(name) {
                this.prefix = '';
                this.say = function () {
                    console.log(`${this.prefix}, i'm ${name}`);
                }
            }
        

Абстрактная фабрика

            function PersonFactory() {
                this.create = function (name, welcomeText) {
                    const person = new Person(name);
                    person.prefix = welcomeText;
                    return person;
                };
            }
        

Абстрактная фабрика

            const personFactory = new PersonFactory();
            const bob = personFactory.create('Bob', 'Hello');
            bob.say();
            const piter = personFactory.create('Piter', 'Hello');
            piter.say();
        

Абстрактная фабрика

            function TreeFactory() {
                this.create = function (branchCount) {
                    const tree = new Tree();
                    tree.branchCount = branchCount;
                    return tree;
                };
            }
            const treeFactory = new TreeFactory();
            const oak = treeFactory.create(5);
        

Строитель

Строитель — это порождающий паттерн проектирования, который позволяет создавать сложные объекты пошагово

Строитель

Строитель

            function Plant() {
                this.build = function(builder) {
                    builder.step1();
                    builder.step2();
                    builder.step3();
                    return builder.get();
                }
            }
        

Строитель

            function RobotBuilder() {
                this.robot = null;
                this.step1 = function() {
                    this.robot = new Robot();
                }
                this.step2 = function() {
                    this.robot.arm = 2;
                }
                this.get = function() {
                    return this.robot;
                }
            }
        

Строитель

            function Robot() {
              this.say = function() {
                console.log(`Hello, I'm robot with ${this.arm} arms`);
              }
            }
        

Строитель

            const director = new Plant();
            const robot = director.build(new RobotBuilder);
            const robot1 = director.build(new RobotBuilder);
            robot.say();
            robot1.say();
        

Прототип

            function Robot() {
              this.name = '';
              this.say = function() {
                console.log(`Hello, I'm a Robot, my name is ${this.name}`);
              }
            }
            const firstRobot = new Robot();
            firstRobot.name = 'Chappy';
            firstRobot.say();
        

Прототип

            function RobotCloneMaker(proto) {
                this.proto = proto;
                this.clone = function() {
                    const robot = new Robot();
                    robot.name = this.proto.name;
                    return robot;
                }
            }
        

Прототип

            const firstRobot = new Robot();
            firstRobot.name = 'Chappy';
            
            const cloneMaker = new RobotCloneMaker(firstRobot);
            const secondRobot = cloneMaker.clone();
            const thirdRobot = cloneMaker.clone();
            secondRobot.say();
            thirdRobot.say();
        

Одиночка

            const simple = {
                a: 1,
                b: 2
            };
            const anotherSimple = simple;
            console.log(anotherSimple, anotherSimple === simple);
        

Одиночка

            function Singletone() {
              if (this.__proto__.instance) return this.__proto__.instance;
             
              this.name = 'Bob';
              this.__proto__.instance = this;
              return this.__proto__.instance;
            }
            const withProto = new Singletone();
            const withProto1 = new Singletone();
            console.log(withProto, withProto1, withProto === withProto1);
        

Одиночка

            class SingletoneOne { // ES7
              static instance;
              constructor() {
                if (instance) return instance;
                this.instance = this;
              }
            }
            const classProto1 = new SingletoneOne();
            const classProto2 = new SingletoneOne();
            console.log(classProto1, classProto1, classProto1 === classProto2);
        

Структурные

Структурные

Адаптер

Адаптер

            function calculator(action, a, b) {
              console.log(action.calc(a, b));
            }
        

Адаптер

            function Division() {
              this.calc = function (a, b) {
                return a / b;
              };
            }
        

Адаптер

            const division = new Division();
            function calculator(action, a, b) {
              console.log(action.calc(a, b));
            }
            calculator(division, 6, 3);
        

Адаптер

            function Substraction() {
              this.first = null;
              this.second = null;
              this.setFirst = function(val) {
                this.first = val;
              }
              this.setSecond = function (val) {
                this.second = val;
              }
              this.calc = function () {
                return this.first - this.second;
              }
            }
        

Адаптер

            /* interface 1 */
            XXX.calc(a, b);// -> result
             
            /* interface 2 */
            YYY.setFirst(a);
            YYY.setSecond(b);
            YYY.calc();// -> result
        

Адаптер

            function SubstractionAdapter(substraction) {
              this.calc = function(a, b) {
                substraction.setFirst(a);
                substraction.setSecond(b);
                return substraction.calc();
              }
            }
        

Мост

Мост — это структурный паттерн проектирования, который разделяет один или несколько классов на две отдельные иерархии — абстракцию и реализацию, позволяя изменять их независимо друг от друга.

Мост

            const cache = new Cache(store);
            cache.set({key: 111}, 'aaa');
            cache.get('aaa');
            cache.clear();
        

Мост

            /** Abstraction */
            function Store(implementation) {
              this.setItem = function(key, value) {
                implementation.setItem(key, value);
              };
              this.getItem = function(key) {
                return implementation.getItem(key);
              };
              this.clear = function() {
                implementation.clear();
              };
            }
        

Мост

            /** Implementation */
            function FileStorage() {
              this.setItem = function (key, value) {
            /** save to file*/
                console.log('saved');
              };
              this.getItem = function (key) {
            /** read from file*/
                console.log('got');
              };
              this.clear = function () {
            /** remove file*/
                console.log('clear');
              };
            }
        

Мост

            /** User code */
            let store;
            if(typeof window != 'undefined') {
              store = new Store(localStorage);
            } else {
              const fileStorage = new FileStorage();
              store = new Store(fileStorage);
            }
            const cache = new Cache(store);
            cache.set({key: 111}, 'aaa');
            cache.get('aaa');
            cache.clear();

        

Декоратор

            function User(name) {
              this.name = name;
              this.say = function() {
                console.log(`Hello, I'm ${this.name}`);
              }
            }
            const bob = new User('Bob');
            bob.say();
        

Декоратор

            function ExtendedUser(user, surname) {
              this.user = user;
              this.name = user.name;
              this.surname = surname;
              this.say = function () {
                console.log(`Hello, I'm ${this.name} ${this.surname}`);
              }
            }
            const Bob = new User('Bob');
            const BobMarko = new ExtendedUser(Bob, 'Marko');
            BobMarko.say();
        

Фасад

            function complicatedFunction1(value) {
              console.log(`Do something very complicated with `);
              return true;
            }
            function complicatedFunction2() {
              console.log('Do something complicated');
            }
            function complicatedFunction3() {
              console.log('Do something else');
            }
        

Фасад

            function Facade(value) {
              this.value = value;
              this.doSomething = function() {
                if (complicatedFunction1(value)) {
                  complicatedFunction2();
                }
                complicatedFunction3();
              }
            }
            const doIt = new Facade('Some data');
            doIt.doSomething();
        

Заместитель

            function ConsoleProxy() {
              this.log = function(message) {
                console.log('======== BEFORE ========');
                console.log(message);
                console.log('======== AFTER ========');
              }
            }
            
            const console2 = new ConsoleProxy();
            console2.log('Hello');

        

Поведенческие

Поведенческие

Цепочка обязанностей

            <div>
              <a href="/" >Link</a>
            </div>
            =========================
            const el = new gQuery('div').find('a').addClass('active').getSelector();
            console.log(el); // -> ???
        

Цепочка обязанностей

            function gQuery(selector) {
              this.selector = selector;
              this.find = function(el) {
                console.log(`Do something to find ""`);
                this.selector = el;
                return this;
              }
              this.addClass = function (className) {
                console.log(`Do something to add className ""`);
                this.selector += `.`;
                return this;
              }
              this.getSelector = function () {
                return this.selector;
              }
            }
        

Команда

Команда

            function add(a, b) {
              return a + b;
            }
            function substract(a, b) {
              return a - b;
            }
        

Команда

            function Command(action, undo, value) {
              this.action = action;
              this.undo = undo;
              this.value = value;
            }
        

Команда

            function AddCommand(value) {
              return new Command(add, substract, value);
            }
        
Пример

Итератор

            function Iterator(items) {
              this.items = items;
              this.index = 0;
            }
        

Итератор

            Iterator.prototype = {
              first: function() {
                this.reset();
                return this.items[this.index];
              },
              last: function () {
                this.index = this.items.length - 1;
                return this.items[this.index];
              },
              next: function() { return this.items[this.index++]; }
              reset: function() { this.index = 0; },
              hasNext: function() { return this.index < this.items.length; }
            };
        

Итератор

            const iterator = new Iterator([1, 2, 3]);
            while(iterator.hasNext()) {
              console.log(iterator.next());
            }
            console.log(iterator.first());
            console.log(iterator.last());
        

Наблюдатель

            function Observer() {
              this.handlers = [];
              this.subscribe = function(fn) {
                this.handlers.push(fn);
              };
              this.unsubscribe = function (fn) {
                this.handlers = this.handlers.filter(item => item.toString() !== fn.toString());
              };
              this.trigger = function (data, context) {
                this.handlers.map(fn => {
                  fn.call(context, data);
                });
              };
            }
        

Наблюдатель

            function Delivery() {
              this.handler = null;
              this.setStrategy = strategy => this.handler = strategy;
              this.calculate = () => this.handler.calculate();
            }
            function NovaPoshta() {
              this.calculate = () => 25;
            }
            function FedEx() {
              this.calculate = () => 500;
            }
        

Наблюдатель

            const delivery = new Delivery();
            delivery.setStrategy(new NovaPoshta());
            console.log(delivery.calculate());
            
            delivery.setStrategy(new FedEx());
            console.log(delivery.calculate());
        

Ууууух

Иммутабельность

Иммутабельность

            const arr = [1, 2, 3, 4, 5, 6];
            function walk(a) {
              a.map(el => console.log(el));
            }
            dirty();
            walk(arr); // -> ???
        

Иммутабельность

            function dirty() {
              console.log('Do some action somewhere in your code and...');
              arr[3] = 100;
            }
        

Иммутабельность

Immutable.JS

Fuctional Programming
hit the high spots

ФП – функциональное программирование

Функциона́льное программи́рование — раздел дискретной математики и парадигма программирования, в которой процесс вычисления трактуется как вычисление значений функций в математическом понимании последних (в отличие от функций как подпрограмм в процедурном программировании).

ФП – функциональное программирование

ФП — это стиль написания программ, при котором просто комбинируется набор функций. В частности, ФП подразумевает обёртывание в функции практически всего подряд.

Функции

Чистые функции

            var increment = 0;
            function autoIncrement() {
              return ++increment;
            }
            var current = autoIncrement();
            console.log(current); // -> ???
        

Чистые функции

autoIncrement() – не чистая функция

Чистые функции

            var increment = 0;
            function autoIncrement(value) { // Чистая функция
              return ++value;
            }
            var current = autoIncrement(increment);
            console.log(current); // -> ???
        

Ссылочная прозрачность

В результате вычисления ссылочно прозрачной функции она дает одно и то же значение для одних и тех же аргументов. Такие функции называются чистыми функциями.

Чистые функции

или ссылочно прозрачные

Функция является чистой если:

Частичное применение

возможность зафиксировать часть аргументов многоместной функции и создать другую функцию, меньшей арности

Арность – количество аргументов функции

Частичное применение

            function add(a, b) {
              return a + b;
            }
            
            function addPartial(value) {
              return function(b) { return add(value, b)}
            }
            
            const add5to = addPartial(5); // -> ???
        

Частичное применение

            function add(a, b) {
              return a + b;
            }
            
            function addPartial(value) {
              return function(b) { return add(value, b)}
            }
            
            const add5to = addPartial(5); // -> ???
            console.log(add5to(3)); // -> ???
            console.log(addPartial(5)(3)); // -> ???
        

Частичное применение

            function volume(width, height, length) {
              return width * height * length;
            }
            console.log(volume(2, 3, 100));
            
            function volumeByLength(length) {
             return function (width, height) { return volume(width, height, length) }
            }
            const volumeByLength100 = volumeByLength(100);
            console.log(volumeByLength100(2,3));

        

Каррирование
!==
Частичное применение

Функции первого класса
высшего порядка

Функции первого класса

Объектами первого класса в контексте конкретного языка программирования называются элементы, которые могут быть переданы как параметр, возвращены из функции, присвоены переменной

Объектами???

Функции первого класса

            const fn = function() {
              //
            }
            const someObject = {
              method: function() { //... }
            }
            fn(someObject.method);
        

Функции высшего порядка

Функция высшего порядка — в программировании функция, принимающая в качестве аргументов другие функции или возвращающая другую функцию в качестве результата. Основная идея состоит в том, что функции имеют тот же статус, что и другие объекты данных.

Хммм

            function addPartial(value) {
              return function(b) { return add(value, b)}
            }
            function calc(action, a, b) { return action(a, b); }
        

Dependency Injection

Example

Мемоизация

Мемоизация — сохранение результатов выполнения функций для предотвращения повторных вычислений

Мемоизация

            function add(a, b) {
              var i = 0;
              while(i < 10000000000) {
                i++;
              }
              return a + b;
            }
        

Мемоизация (example)

            function memoization(fn) {
              const cache = {};
              return function(...args) {
                const hash = args.join('-');  // ???
                if (cache[hash]) {
                  console.log('Get data from cache');
                  return cache[hash];
                }
                console.log('Calculate data');
                cache[hash] = fn(...args);
                return cache[hash];
              }
            }
        

That's All Folks

Самостоятельное изучение

Спасибо!

Denis Zavgorodny
web technologist, founder of @ChernivtsiJS,
mentor at @NodeSchool Chernivtsi

@DenisZavgorodny

denis.zavgorodny@gmail.com