JavaScript Design Patterns

Автор Artem Malieiev

Что такое паттерны?

Паттерны это

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

Банда четырёх

1995

Классические Паттерны

  • — поведенческие
  • — порождающие
  • — структурные

Паттерны в JavaScript

Всего чуть-чуть, на донышке

Module


                (function (global, undefined) {

                    //Code goes here...

                }(this));
            

provides incapsulated scope and possibility of retruned value


                var m1 = (function (global, undefined) {
                    var myAwesomeModule = {};

                    myAwesomeModule.awesome = function () {
                        console.log("doing awesome things...");
                    };

                    return myAwesomeModule;
                }(this));

                console.log(myAwesomeModule); //ReferenceError
                m1.awesome(); //doing awesome things...
                

Constructor


                function Car(make) {
                    this.make = make;
                };

                Car.prototype.start = function () {
                    console.log(this.make + ' is starting...');
                };
                

                var car = new Car('bmw');

                car.start(); //bmw is starting...
                

IIFE


                (function(){ /* useful code */ }());
            

                var Car = (function () {
                    function Car() {}

                    Car.prototype.start = function () {};

                    return Car;
                }());

                var car = new Car('bmw');
            

Closure


                function multiplier(baseValue) {
                    return function (value) {
                        return baseValue * value;
                    }
                }

                var doubler = multiplier(2);

                doubler(5); // 10
                doubler(3); // 6
            

Mixin


        var flyable = function () {
            this.fly = function () {
                console.log("I'm flying through the clouds...");
            }
        };
            

        function Car (make) {
            this.make = make;
            //use mixin
            flyable.call(this);
        };

        var car  = new Car('bmw');
        car.fly(); //I'm flying through the clouds...
            

Callback


                function callback(data) {
                    console.log(data);
                }

                //events
                window.onload = callback;

                //async operation
                jQuery.ajax.get('some-url.com', callback);

                //regular sync operations
                function sum(value1, value2, callback) {
                    var result = value1 + value2;
                    callback(result);
                }
            

А где паттерны?

Singleton


                const singleton = {

                    //properties and methods...

                };

                export default singleton;
            

userProfile.js


                const userProfile = {
                    name: 'John Smith',
                    id: '123',
                    logged: true,
                    isLogged() {
                        return this.isLogged();
                    }
                };

                export default userProfile;
            

main.js


                import user from './userProfile';

                user.name //John Smith
            

Factory


        export default function objectsFactory(type) {
            switch (type) {
                case 'type1': return new ConcreteType1();
                case 'type2': return new ConcreteType2();
                case 'type3': return new ConcreteType3();
                default: return new ConcreteTypeDefault();
            }
        }
            

carsFactory.js


    export default function carsFactory(make) {
        switch (make) {
            case 'bmw': return new BmwX6();
            case 'rangeRover': return new RangeRowerSport();
            case 'audi': return new AudiTT();
            default: return new Tesla();
        }
    }
            

main.js


    import carsFactory from './carsFactory';

    const car = carsFactory('bmw');
            

Observer


    function Observer() {
        this.events = {};
        this.on = function (event, callback) {};
        this.off = function (event, callback) {};
        this.emit = function (event, payload) {};
    }

    export default Observer;
            

on


    function Observer() {
        ...
        this.on = function (event, callback) {
            if (!this.events[event]) {
                this.events[event] = [];
            }
            this.events[event].push(callback);
        };
        ...
    }
            

off


function Observer() {
    ...
    this.off = function (event, callback) {
        if (this.events[event]
                && this.events[event].indexOf(callback) !== -1) {
            this.events[event]
                .splice(this.events[event].indexOf(callback), 1);
        }
    };
    ...
}
            

emit


    function Observer() {
        ...
        this.emit = function (event, payload) {
            if(this.events[event]) {
                this.events[event].forEach((callback) => {
                    callback(payload);
                });
            }
        };
    }
            

NewsChanel.js


    import Observer from './Observer';

    export function NewsChanel() {
        Observer.call(this);
    }
            

main.js


    import NewsChanel from './NewsChanel';

    const log = data => console.log(data);
    const chanel = new NewsChanel();

    chanel.on('news', log);
    chanel.emit('news', 'lorem ipsum...');//lorem ipsum...
    chanel.off('news', log);
    chanel.emit('news', 'lorem ipsum...');//undefined
            

Mediator


        const Mediator = {
            register(member) {
                member.on('event', payload => {
                    otherMember.doSomething(payload);
                });
            }
        };

        class Member {
            constructor() {
                Observer.call(this);
            }
        }
            

Dealer.js


                const Dealer = {
                    buyers: [],
                    sellers: [],
                    register(member) {},
                    request(product) {}
                };
            

register


        const Dealer = {
            ...
            register(member) {
                member.on('sell', product => {
                    this.sellers.push({ member, product });
                    this.request(product);
                });
                member.on('buy', product => {
                    this.buyers.push({ member, product });
                    this.request(product);
                });
            }
            ...
        };
            

request


const Dealer = {
    request(product) {
        const seller = this.sellers.find(seller =>
            seller.product === product);
        const buyer = this.buyers.find(buyer => {
            buyer.product === product);
        if (seller && buyer) {
            seller.member.sell(product, buyer.member);
            buyer.member.buy(product, seller.member);
            this.sellers.splice(this.sellers.indexOf(seller), 1);
            this.buyers.splice(this.buyers.indexOf(buyer), 1);
        }
    }
};
            

Member.js


    class Member {
        constructor(name) {
            this.name = name;
            Observer.call(this);
        }
        sell(product, buyer) {
            console.log(`
                ${this.name} sells ${product} to ${buyer.name}`);
        }
        buy(product, seller) {
            console.log(`
                ${this.name} buys ${product} from ${seller.name}`);
        }
    }
            

main.js


        const fill = new Member('fill');
        const adam = new Member('adam');
        const joe = new Member('joe');

        Dealer.register(fill);
        Dealer.register(adam);
        Dealer.register(joe);

        fill.emit('sell', 'bmw');
        fill.emit('buy', 'iphone');
        adam.emit('buy', 'audi');
        adam.emit('sell', 'iphone');// adam sells iphone to fill
                                    // fill buys iphone from adam
            

Proxy


                const original = {
                    //properties and methods
                };

                const proxy = new Proxy(original, {
                    set(obj, prop, value) {
                        //intercession
                        obj[prop] = value;
                    },
                    get(obj, prop) {
                        //intercession
                        return obj[prop];
                    }
                });
            
https://developer.mozilla.org/.../Proxy

Human.js


class Human {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }

    greet() {
        console.log(`
            Hello from ${this.name}, I'm ${this.age} years old!`);
    }
}

export default Human;
            

HumanProxy.js


    class HumanProxy {
        constructor(human) {
            return new Proxy(human, {
                set(obj, prop, value) {
                    if (prop === 'age') {
                        if (Number.isInteger(value) && value > 0) {
                            obj[prop] = value;
                        }
                    }
                }
            });
        }
    }

    export default HumanProxy;
            

main.js


        import Human from './Human';
        import HumanProxy from './HumanProxy';

        const human = new Human('Joe', 23);
        const humanProxy = new HumanProxy(human);

        humanProxy.age// 23
        humanProxy.age = -100500;
        humanProxy.greet();//Hello from Joe, I'm 23 years old!
        humanProxy.age = 30;
        humanProxy.greet();//Hello from Joe, I'm 30 years old!

        human// {name: 'Joe', age: 30}
            

MVC

Эволюция MVC

MVVM

https://ru.wikipedia.org/wiki/Model-View-ViewModel

Популярные фреймверки

     

Flux

Redux

http://redux.js.org/

Vuex

https://vuex.vuejs.org

Литература

Вопросы?

Эта презентация доступна по адресу - http://amalieiev.github.io/javascript-design-patterns-presentation

Презентация создана с помощью Reveal.js

Спасибо за внимание