设计模式是针对常见软件问题的高级面向对象解决方案。模式是关于对象的可重用设计和交互。在讨论复杂的设计解决方案时,每个模式都有一个名称并成为词汇表的一部分。
大多数情况下,它们遵循原始图案设计的结构和意图。这些示例演示了每种模式背后的原则,但并未针对 JavaScript 进行优化。
Abstract Factory
Abstract Factory创建由共同主题相关的对象。在面向对象编程中,工厂是创建其他对象的对象。抽象工厂抽象出新创建的对象共享的主题。
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(); }}
Builder
Builder 模式允许客户端仅通过指定类型和内容来构建复杂对象,细节完全对客户隐藏。
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();}
Factory Method
Factory Method 按照客户的指示创建新对象。在 JavaScript 中创建对象的一种方法是使用 new 运算符调用构造函数。
然而,在某些情况下,客户端不知道或不应知道要实例化多个候选对象中的哪一个。
Factory Method 允许客户端委托对象创建,同时仍然保留对要实例化的类型的控制。
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 = "";};var PartTime = function () { this.hourly = "";};var Temporary = function () { this.hourly = "";};var Contractor = function () { this.hourly = "";};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(); }}
Adapter
Adapter模式将一个接口(对象的属性和方法)转换为另一个接口。Adapter允许编程组件协同工作,否则由于接口不匹配而无法协同工作。适配器(Adapter)模式也称为包装器模式。
// old interfacefunction Shipping() { this.request = function (zipStart, zipEnd, weight) { // ... return ".75"; }}// new interfacefunction AdvancedShipping() { this.login = function (credentials) { /* ... */ }; this.setStart = function (start) { /* ... */ }; this.setDestination = function (destination) { /* ... */ }; this.calculate = function (weight) { return ".50"; };}// adapter interfacefunction 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);}
Decorator
Decorator模式动态地扩展(装饰)对象的行为。在运行时添加新行为的能力是由 Decorator 对象实现的,它“将自身包装”在原始对象周围。多个装饰器可以向原始对象添加或覆盖功能。
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();}
Facade
Facade 模式提供了一个接口,使客户免受一个或多个子系统中复杂功能的影响。这是一个看似微不足道但功能强大且极其有用的简单模式。它通常出现在围绕多层架构构建的系统中。
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("0,000"); console.log(result);}
Proxy
代理模式为另一个对象提供代理或占位符对象,并控制对另一个对象的访问。
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());}
Mediator
Mediator模式通过封装这些对象的交互方式来提供对一组对象的集中管理权。此模型对于需要管理复杂条件的场景很有用,在这种情况下,每个对象都知道组中任何其他对象的任何状态更改。
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);}
Observer
Observer模式提供了一种订阅模型,其中对象订阅一个事件并在事件发生时得到通知。这种模式是事件驱动编程的基石,包括 JavaScript。Observer模式促进了良好的面向对象设计并促进了松散耦合。
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');}
Visitor
Visitor模式定义了对对象集合的新操作,而不更改对象本身。新逻辑驻留在一个名为 Visitor 的单独对象中。
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"); }}
当我们结束我们的 JavaScript 设计模式之旅时,很明显这些强大的工具在制作可维护、可扩展和高效的代码方面发挥着至关重要的作用。
通过理解和实施这些模式,您不仅会提升您的编程技能,还会为您自己和您的团队成员创造更愉快的开发体验。