Share 10 common JavaScript design patterns

59fa0150b3f77d81ac19489e2fd82e50.jpeg

English | https://blog.bitsrc.io/10-javascript-design-patterns-3087d1dda5b4

introduce

Design patterns are high-level object-oriented solutions to common software problems. Patterns are about reusable design and interaction of objects. Each pattern has a name and becomes part of the vocabulary when discussing complex design solutions.

In this tutorial, I provide JavaScript examples for each GoF pattern. For the most part, they follow the structure and intent of the original pattern design. These examples demonstrate the principles behind each pattern, but are not optimized for JavaScript.

01.Abstract Factory 

Abstract Factory creates objects related by a common theme. In object-oriented programming, factories are objects that create other objects. An Abstract Factory abstracts away the topics shared by newly created objects.

62a1872b55e07d9e8315a6d9b65c7fc8.png

function Employee(name) {
    this.name = name;


    this.say = function () {
        console.log("I am employee " + name);
    };
}


function EmployeeFactory() {


    this.create = function (name) {
        return new Employee(name);
    };
}


function Vendor(name) {
    this.name = name;


    this.say = function () {
        console.log("I am vendor " + name);
    };
}


function VendorFactory() {


    this.create = function (name) {
        return new Vendor(name);
    };
}


function run() {
    var persons = [];
    var employeeFactory = new EmployeeFactory();
    var vendorFactory = new VendorFactory();


    persons.push(employeeFactory.create("Joan DiSilva"));
    persons.push(employeeFactory.create("Tim O'Neill"));
    persons.push(vendorFactory.create("Gerald Watson"));
    persons.push(vendorFactory.create("Nicole McNight"));


    for (var i = 0, len = persons.length; i < len; i++) {
        persons[i].say();
    }
}

02.Builder 

The Builder pattern allows clients to build complex objects only by specifying the type and content, with details completely hidden from clients.

8074a7930743f0feff24ec8c43d48681.png

function Shop() {
    this.construct = function (builder) {
        builder.step1();
        builder.step2();
        return builder.get();
    }
}


function CarBuilder() {
    this.car = null;


    this.step1 = function () {
        this.car = new Car();
    };


    this.step2 = function () {
        this.car.addParts();
    };


    this.get = function () {
        return this.car;
    };
}


function TruckBuilder() {
    this.truck = null;


    this.step1 = function () {
        this.truck = new Truck();
    };


    this.step2 = function () {
        this.truck.addParts();
    };


    this.get = function () {
        return this.truck;
    };
}


function Car() {
    this.doors = 0;


    this.addParts = function () {
        this.doors = 4;
    };


    this.say = function () {
        console.log("I am a " + this.doors + "-door car");
    };
}


function Truck() {
    this.doors = 0;


    this.addParts = function () {
        this.doors = 2;
    };


    this.say = function () {
        console.log("I am a " + this.doors + "-door truck");
    };
}


function run() {
    var shop = new Shop();
    var carBuilder = new CarBuilder();
    var truckBuilder = new TruckBuilder();
    var car = shop.construct(carBuilder);
    var truck = shop.construct(truckBuilder);


    car.say();
    truck.say();
}

03、Factory Method 

The Factory Method creates new objects as directed by the client. One way to create objects in JavaScript is to call a constructor using the new operator. 

In some cases, however, the client does not know or should not know which of multiple candidates to instantiate. 

Factory Method allows clients to delegate object creation while still retaining control over the types to instantiate.

9dc10b537d23f8344b697acc76ded73f.png

var Factory = function () {
    this.createEmployee = function (type) {
        var employee;


        if (type === "fulltime") {
            employee = new FullTime();
        } else if (type === "parttime") {
            employee = new PartTime();
        } else if (type === "temporary") {
            employee = new Temporary();
        } else if (type === "contractor") {
            employee = new Contractor();
        }


        employee.type = type;


        employee.say = function () {
            console.log(this.type + ": rate " + this.hourly + "/hour");
        }


        return employee;
    }
}


var FullTime = function () {
    this.hourly = "$12";
};


var PartTime = function () {
    this.hourly = "$11";
};


var Temporary = function () {
    this.hourly = "$10";
};


var Contractor = function () {
    this.hourly = "$15";
};


function run() {


    var employees = [];
    var factory = new Factory();


    employees.push(factory.createEmployee("fulltime"));
    employees.push(factory.createEmployee("parttime"));
    employees.push(factory.createEmployee("temporary"));
    employees.push(factory.createEmployee("contractor"));


    for (var i = 0, len = employees.length; i < len; i++) {
        employees[i].say();
    }
}

04、Adapter

The Adapter pattern converts one interface (properties and methods of an object) into another interface. Adapters allow programmatic components to work together that otherwise would not work together due to interface mismatches. The Adapter pattern is also known as the Wrapper pattern.

9636d6b72c204556752bd991d8a65ec4.png

// old interface


function Shipping() {
    this.request = function (zipStart, zipEnd, weight) {
        // ...
        return "$49.75";
    }
}


// new interface


function AdvancedShipping() {
    this.login = function (credentials) { /* ... */ };
    this.setStart = function (start) { /* ... */ };
    this.setDestination = function (destination) { /* ... */ };
    this.calculate = function (weight) { return "$39.50"; };
}


// adapter interface


function ShippingAdapter(credentials) {
    var shipping = new AdvancedShipping();


    shipping.login(credentials);


    return {
        request: function (zipStart, zipEnd, weight) {
            shipping.setStart(zipStart);
            shipping.setDestination(zipEnd);
            return shipping.calculate(weight);
        }
    };
}


function run() {


    var shipping = new Shipping();
    var credentials = { token: "30a8-6ee1" };
    var adapter = new ShippingAdapter(credentials);


    // original shipping object and interface


    var cost = shipping.request("78701", "10010", "2 lbs");
    console.log("Old cost: " + cost);


    // new shipping object with adapted interface


    cost = adapter.request("78701", "10010", "2 lbs");


    console.log("New cost: " + cost);
}

05、Decorator

The Decorator pattern dynamically extends (decorates) the behavior of objects. The ability to add new behavior at runtime is implemented by Decorator objects, which "wrap themselves" around the original object. Several decorators can add or override functionality to the original object.

133474c779b16b9b203c130448fd526b.png

var User = function (name) {
    this.name = name;


    this.say = function () {
        console.log("User: " + this.name);
    };
}


var DecoratedUser = function (user, street, city) {
    this.user = user;
    this.name = user.name;  // ensures interface stays the same
    this.street = street;
    this.city = city;


    this.say = function () {
        console.log("Decorated User: " + this.name + ", " +
            this.street + ", " + this.city);
    };
}


function run() {


    var user = new User("Kelly");
    user.say();


    var decorated = new DecoratedUser(user, "Broadway", "New York");
    decorated.say();
}

06、Facade

The Facade pattern provides an interface that shields clients from complex functionality in one or more subsystems. This is a simple pattern that may seem trivial but is powerful and extremely useful. It typically occurs in systems built around a multi-tier architecture.

c4fe1043e32cd4d5ff5a9da338cd6dd0.png

var Mortgage = function (name) {
    this.name = name;
}


Mortgage.prototype = {


    applyFor: function (amount) {
        // access multiple subsystems...
        var result = "approved";
        if (!new Bank().verify(this.name, amount)) {
            result = "denied";
        } else if (!new Credit().get(this.name)) {
            result = "denied";
        } else if (!new Background().check(this.name)) {
            result = "denied";
        }
        return this.name + " has been " + result +
            " for a " + amount + " mortgage";
    }
}


var Bank = function () {
    this.verify = function (name, amount) {
        // complex logic ...
        return true;
    }
}


var Credit = function () {
    this.get = function (name) {
        // complex logic ...
        return true;
    }
}


var Background = function () {
    this.check = function (name) {
        // complex logic ...
        return true;
    }
}


function run() {
    var mortgage = new Mortgage("Joan Templeton");
    var result = mortgage.applyFor("$100,000");


    console.log(result);
}

07、Proxy

The proxy pattern provides a proxy or placeholder object for another object and controls access to another object.

fbb80f21dd7dc3f3511b41e269e9ac58.png

function GeoCoder() {


    this.getLatLng = function (address) {


        if (address === "Amsterdam") {
            return "52.3700° N, 4.8900° E";
        } else if (address === "London") {
            return "51.5171° N, 0.1062° W";
        } else if (address === "Paris") {
            return "48.8742° N, 2.3470° E";
        } else if (address === "Berlin") {
            return "52.5233° N, 13.4127° E";
        } else {
            return "";
        }
    };
}


function GeoProxy() {
    var geocoder = new GeoCoder();
    var geocache = {};


    return {
        getLatLng: function (address) {
            if (!geocache[address]) {
                geocache[address] = geocoder.getLatLng(address);
            }
            console.log(address + ": " + geocache[address]);
            return geocache[address];
        },
        getCount: function () {
            var count = 0;
            for (var code in geocache) { count++; }
            return count;
        }
    };
};


function run() {


    var geo = new GeoProxy();


    // geolocation requests


    geo.getLatLng("Paris");
    geo.getLatLng("London");
    geo.getLatLng("London");
    geo.getLatLng("London");
    geo.getLatLng("London");
    geo.getLatLng("Amsterdam");
    geo.getLatLng("Amsterdam");
    geo.getLatLng("Amsterdam");
    geo.getLatLng("Amsterdam");
    geo.getLatLng("London");
    geo.getLatLng("London");


    console.log("\nCache size: " + geo.getCount());


}

08、Mediator

The Mediator pattern provides centralized management of a set of objects by encapsulating how these objects interact. This model is useful for scenarios where complex conditions need to be managed, where each object is aware of any state change of any other object in the group.

56dc5154eafca5f801da3c091c4464de.png

var Participant = function (name) {
    this.name = name;
    this.chatroom = null;
};


Participant.prototype = {
    send: function (message, to) {
        this.chatroom.send(message, this, to);
    },
    receive: function (message, from) {
        console.log(from.name + " to " + this.name + ": " + message);
    }
};


var Chatroom = function () {
    var participants = {};


    return {


        register: function (participant) {
            participants[participant.name] = participant;
            participant.chatroom = this;
        },


        send: function (message, from, to) {
            if (to) {                      // single message
                to.receive(message, from);
            } else {                       // broadcast message
                for (key in participants) {
                    if (participants[key] !== from) {
                        participants[key].receive(message, from);
                    }
                }
            }
        }
    };
};


function run() {


    var yoko = new Participant("Yoko");
    var john = new Participant("John");
    var paul = new Participant("Paul");
    var ringo = new Participant("Ringo");


    var chatroom = new Chatroom();
    chatroom.register(yoko);
    chatroom.register(john);
    chatroom.register(paul);
    chatroom.register(ringo);


    yoko.send("All you need is love.");
    yoko.send("I love you John.");
    john.send("Hey, no need to broadcast", yoko);
    paul.send("Ha, I heard that!");
    ringo.send("Paul, what do you think?", paul);
}

09、Observer

The Observer pattern provides a subscription model where objects subscribe to an event and are notified when the event occurs. This pattern is the cornerstone of event-driven programming, including JavaScript. The Observer pattern promotes good object-oriented design and promotes loose coupling.

b722e2f998c5216e4304f2f9aa8e4964.png

function Click() {
    this.handlers = [];  // observers
}


Click.prototype = {


    subscribe: function (fn) {
        this.handlers.push(fn);
    },


    unsubscribe: function (fn) {
        this.handlers = this.handlers.filter(
            function (item) {
                if (item !== fn) {
                    return item;
                }
            }
        );
    },


    fire: function (o, thisObj) {
        var scope = thisObj || window;
        this.handlers.forEach(function (item) {
            item.call(scope, o);
        });
    }
}


function run() {


    var clickHandler = function (item) {
        console.log("fired: " + item);
    };


    var click = new Click();


    click.subscribe(clickHandler);
    click.fire('event #1');
    click.unsubscribe(clickHandler);
    click.fire('event #2');
    click.subscribe(clickHandler);
    click.fire('event #3');
}

10、Visitor

The Visitor pattern defines new operations on collections of objects without changing the objects themselves. The new logic resides in a separate object called Visitor.

4b3895465aa188bc8d75bc4d97f719fb.png

var Employee = function (name, salary, vacation) {
    var self = this;


    this.accept = function (visitor) {
        visitor.visit(self);
    };


    this.getName = function () {
        return name;
    };


    this.getSalary = function () {
        return salary;
    };


    this.setSalary = function (sal) {
        salary = sal;
    };


    this.getVacation = function () {
        return vacation;
    };


    this.setVacation = function (vac) {
        vacation = vac;
    };
};


var ExtraSalary = function () {
    this.visit = function (emp) {
        emp.setSalary(emp.getSalary() * 1.1);
    };
};


var ExtraVacation = function () {
    this.visit = function (emp) {
        emp.setVacation(emp.getVacation() + 2);
    };
};


function run() {


    var employees = [
        new Employee("John", 10000, 10),
        new Employee("Mary", 20000, 21),
        new Employee("Boss", 250000, 51)
    ];


    var visitorSalary = new ExtraSalary();
    var visitorVacation = new ExtraVacation();


    for (var i = 0, len = employees.length; i < len; i++) {
        var emp = employees[i];


        emp.accept(visitorSalary);
        emp.accept(visitorVacation);
        console.log(emp.getName() + ": $" + emp.getSalary() +
            " and " + emp.getVacation() + " vacation days");
    }
}

in conclusion

As we wrap up our tour of JavaScript design patterns, it's clear that these powerful tools play a vital role in making code maintainable, extensible, and efficient.

By understanding and implementing these patterns, you will not only improve your programming skills, but also create a more enjoyable development experience for yourself and your team members.

Remember that design patterns are not a one-size-fits-all solution. It is critical to analyze the unique needs and constraints of a project to determine which patterns will bring the most value.

Constantly learning and experimenting with different design patterns will enable you to make informed decisions and choose the best approach for your project.

Integrating design patterns into your workflow may take an investment of time and effort, but it will be worth it in the long run.

When you master the art of writing elegant, modular, and efficient JavaScript code, you'll find that your applications become more robust, your debugging process more manageable, and your overall development experience more enjoyable.

So go ahead and explore the world of JavaScript design patterns and hope your code is more maintainable, scalable and efficient.

Thanks for reading, and happy programming!

learn more skills

Please click the public number below

931d88cdbf6c5c94206b627e149b588a.gif

Guess you like

Origin blog.csdn.net/Ed7zgeE9X/article/details/130716584