JavaScript中的设计模式
#javascript #编程 #codenewbie #codequality

嘿ð

自从我开始专业编码以来已经有几年了。我一直在对话中听过“设计模式”一词,但从未真正尝试学习它们。直到最近,一位高级开发人员在工作中审查了我的公关,并留下了一条评论,说“……试图使这成为单身人”。这使我终于对这个话题进行了一些挖掘。

我经历了许多资源,例如崩溃课程,文章和YouTube视频。令我惊讶的是,我发现我一直在不知不觉中使用其中一些模式。我很确定您也会有同样的感觉。我肯定有一些新的想法/模式,我发现有用。在这篇文章中,我将尝试介绍一些模式,这些模式肯定会帮助您成为更好的开发人员,甚至引起您的好奇心,以了解更多此类模式。


什么是设计模式?

设计模式是针对常见软件架构问题进行战斗测试的蓝图。他们提供了一个抽象概念来解决问题。由开发人员建立在他们身上并提出实际解决方案。

与算法不同,这些模式没有提供清晰的步骤来解决问题。这些只是您可以根据需要进一步扩展的设计想法。

其中一些模式在下面列出。

1.单例图案

singleton模式可确保类只有一个实例,并且该实例在整个应用程序中都可以在全球范围内访问。此单例的导入均参考相同的实例。

class LocalStorage {
  constructor() {
    if (!LocalStorage.instance) {
      LocalStorage.instance = this;
    }
    return LocalStorage.instance;
  }

  setItem(key, value) {
    // save to some storage
    console.log(`Item saved: ${key} - ${value}`)
  }

  getItem(key) {
    console.log(`Getting item ${key} from storage.`);
    // get from storage and return
  }
}

const store = Object.freeze(new LocalStorage());

export default store;

在本地存储周围实现自己的包装器是单例模式的一个很好的例子。我们要使用宽的同一实例应用程序。

在这里,我们创建一个LocalStorage实例并导出它。无法创建此类的另一个对象。即使他们尝试过,constructor也会阻止它们,因为它检查了实例是否已经存在。

最后,Object.freeze是蛋糕上的樱桃。它将阻止对我们的对象进行任何修改。

ðâstrong>使用:

  1. 获得单个实例的全局访问点。
  2. 单身人士仅在请求时才初始化一次。因此,它节省了内存空间。

ð»缺点:

  1. 很难为单身人士编写测试,因为我们不能每次创建新实例。所有测试都取决于一个全局实例。

2.代理模式

代理模式,让您为目标对象提供替代/包装器。使用这种模式可以拦截或重新定义与目标对象的所有交互。

JavaScript具有内置的ProxyReflect对象来实现此模式。

const target = {
  name: 'John',
  age: 82
}

const handler = {
 get: (target, key) => {
   console.log(`Getting key: ${key}`);
   return target[key];
 },
 set: (target, key, value) => {
   console.log(`Setting ${value} on key ${key}`);
   target[key] = value;
   return true;
 }
};

const proxyObject = new Proxy(target, handler);

console.log(proxyObject.name);

在上面的代码中,我们向Proxy提供一个target对象和一个handler对象。 handler对象可用于覆盖-getsethasdeleteProperty等对象方法的实现。

另外,如果我们不希望像上面的示例一样修改这些方法的行为,则可以使用Reflect。它具有完全相同的功能。

const handler = {
 get: (target, key) => {
   console.log(`Getting key: ${key}`);
   return Reflect.get(target, key);
 },
 set: (target, key, value) => {
   console.log(`Setting ${value} on key ${key}`);
   return Reflect.set(target, key, value);
 }
};

ðâstrong>使用:

  1. 访问控制
  2. 记录请求
  3. Caching
  4. 懒惰初始化

ð»缺点:

  1. 这可能导致性能问题。
  2. 该代码可能会随着太多代理而变得复杂。

3.观察者模式

观察者模式是一种设计模式,其中称为主题或发布者的对象维护其依赖者列表,称为观察者或订阅者,并自动通知他们对其状态的任何更改。当另一个对象的状态更改时,可以通知多个对象,而无需直接引用彼此。

这个模式的简单示例看起来像这样。

class Button {
  constructor() {
    this.subscribers = [];
  }

  subscribe(fn) {
    this.subscribers.push(fn);
  }

  doSomething() {
    const data = { x: 1 };
    this.notify(data);
  }

  notify(data) {
    this.subscribers.forEach(fn => fn(data));
  }
}


class EventHandler {
  listener(data) {
     console.log("Received:", data);
  }
}

const button = new Button();

const eventHandler = new EventHandler();

button.subscribe(eventHandler.listener);

button.doSomething();
// Received: {x : 1}

在此示例中,Button跟踪其所有订户,并通过调用notify方法通知他们任何更改。

ðâstrong>使用:

  1. 当一个对象的状态变化对于其他对象很重要,并且这些对象事先未知时,可以使用此模式。

ð»缺点:

  1. 这可能会导致某些性能问题,如果订户过多或notify函数中的某些复杂代码。
  2. 无法预测这些订户收到的通知的顺序。

4.工厂模式

出厂模式是一种设计模式,它提供了一种创建对象的方法,而无需指定将要创建的确切对象类别。相反,工厂类/功能负责创建对象。

imo,这是此处讨论的所有模式中最简单的一种。你们中许多人已经在使用它很有可能。

class Car {
  constructor(make, model) {
    this.make = make;
    this.model = model;
  }
}

class Truck {
  constructor(make, model, payloadCapacity) {
    this.make = make;
    this.model = model;
    this.payloadCapacity = payloadCapacity;
  }
}

function vehicleFactory(type, make, model, payloadCapacity) {
  switch (type) {
    case "car":
      return new Car(make, model);
    case "truck":
      return new Truck(make, model, payloadCapacity);
    default:
      throw new Error(`Invalid vehicle type: ${type}`);
  }
}

const car = vehicleFactory("car", "Toyota", "Camry");
console.log(car);
// Output: Car { make: "Toyota", model: "Camry" }

const truck = vehicleFactory("truck", "Ford", "F-150", 2000);
console.log(truck); 
// Output: Truck { make: "Ford", model: "F-150", payloadCapacity: 2000 }

而不是直接访问CarTruck类,而是将该任务委派给我们的出厂功能。

ðâstrong>使用:

  1. 几乎可以在没有任何开销的任何地方使用。
  2. 执行干燥。

ð»缺点:我看不到! ð

5.原型模式

原型图案是一种设计模式,可以克隆或复制对象,而不是从头开始创建。原型设计模式依赖于JavaScript prototypical inheritance。此模式主要用于在性能密集型情况下创建对象。

JavaScript中原型模式的一个示例是代表基本形状(例如矩形)的原型对象,以及根据原型创建新形状的函数。

const shapePrototype = {
  width: 0,
  height: 0,
  area: function() {
    return this.width * this.height;
  }
};

function createShape(shape) {
  const newShape = Object.create(shapePrototype);
  newShape.width = shape.width;
  newShape.height = shape.height;
  return newShape;
}

const shape1 = createShape({width: 5, height: 10});
console.log(shape1.area()); 
// Output: 50

const shape2 = createShape({width: 3, height: 4});
console.log(shape2.area());
// Output: 12

createShape函数以对象为参数,该对象用于设置新形状的widthheight属性。新形状对象从原型继承了area方法。

您可以通过检查对象的原型来验证这一点。

console.log(shapePrototype.__proto__)
// This is for you to find out! ;)

console.log(shape1.__proto__)
// Output: {
//  area: function() {
//    return this.width * this.height;
//  },
//  height: 0,
//  width: 0
// }

console.log(shape1.__proto__.__proto__ === shapePrototype.__proto__)
// Output: true

ðâstrong>使用:

  1. 内存有效,因为多个对象依赖相同的原型并共享相同的方法或属性。

ð»缺点:

  1. 如果有一个长长的原型链,很难知道某些属性来自何处。

总而言之,设计模式是组织和构造代码的强大工具,使其更可读,可维护和可重复使用。本文涵盖的五个模式只是JavaScript中许多设计模式的一些示例。通过了解这些模式以及如何应用它们,您可以提高代码的质量,并使其更加稳健和高效。

我希望这篇文章有所帮助且内容丰富。我将来将继续撰写有关其他设计模式的文章,并可以随意在LinkedInTwitter上与我建立联系,在那里我分享了我的工作和文章的想法和更新。

谢谢! ð