首页 > web前端 > js教程 > 揭秘设计模式

揭秘设计模式

Susan Sarandon
发布: 2024-11-03 10:54:02
原创
343 人浏览过

所以人工智能时代已经到来,这是一个巨大的飞跃,目前,用 const fetch = require('node-fetch') 吐出 Node 代码? (对于今天的 ChatGPT 和 Gemini 来说都是如此)并为循环机器的另一个旋转提供动力,即互联网及其内容。

在内容的融合中,设计模式再次出现

Demystifying Design Patterns

从解释如何在 Node(???) 中应用设计模式的帖子到解释所有详细过时内容的帖子,例如如何在 Java 中应用工厂模式(Java 8 于 2014 年 3 月发布添加了 Lambda) ).

定义

曾经偶然遇到过重构大师吗?
这是您在计算机科学(特别是编程)学习之旅中可能访问过的网站。它的设计模式部分得到了很好的解释,也是多年来通过不同论坛分享最多的部分之一。

如果我们去定义什么是设计模式,我们会发现:

设计模式是常见问题的典型解决方案
在软件设计中。每一个图案都像一张蓝图
您可以自定义以解决特定问题
代码中的设计问题。

那为什么要发这篇文章呢?我的意思是,上面链接的网站上有很多信息;这可能就是全部了。

问题是,我总是很难接受这个定义...“在我的代码中解决特定的设计问题”...在我的代码中?我的代码是否有需要解决的问题?

定义,重新构想

真正发生的事情是,我需要编写某个“东西”

,项目中使用的编程语言缺乏的抽象。

简单明了。以防万一您还没有产生共鸣,让我们看一些带有代码的示例。

这是 Java(主要是一种面向对象的编程语言)中工厂模式的一个非常简单的实现。


public class ShapeFactory {
  public Shape createShape(String type) {
    if (type.equalsIgnoreCase("CIRCLE")) {
      return new Circle();
    } else if (type.equalsIgnoreCase("SQUARE")) {
      return new Square();
    } 
    return null;   
  }
}
登录后复制
登录后复制
然后 Java 8

(2014 年 3 月,以防万一你忘记了) 添加了 Lambda(函数式编程的一个概念),所以我们可以这样做:

Map<String, Supplier<Shape>> shapeFactory = new HashMap<>();
shapeFactory.put("CIRCLE", Circle::new);
shapeFactory.put("SQUARE", Square::new);

Shape circle = shapeFactory.get("CIRCLE").get();
登录后复制
登录后复制
不再需要工厂设计模式(至少在 Java 中)。

是的,我知道工厂模式是大多数人一直使用的示例,但是其他模式会发生什么情况呢?在其他编程语言中会发生什么?

这是 Typescript 中的访问者模式:


interface Shape {
  draw(): void;
  accept(visitor: ShapeVisitor): void; 
}

class Circle implements Shape {
  radius: number;

  constructor(radius: number) {
    this.radius = radius;   

  }

  draw() {
    console.log("Drawing a circle");
  }

  accept(visitor: ShapeVisitor) {
    visitor.visitCircle(this); 
  }
}

class Square implements Shape {
  sideLength: number;

  constructor(sideLength: number) {
    this.sideLength = sideLength;
  }

  draw() {
    console.log("Drawing a square");
  }

  accept(visitor: ShapeVisitor) {
    visitor.visitSquare(this);
  }
}

interface ShapeVisitor {
  visitCircle(circle: Circle): void;
  visitSquare(square: Square): void;
}

class AreaCalculator implements ShapeVisitor {
  private area = 0;

  visitCircle(circle: Circle) { 
    this.area = Math.PI * circle.radius * circle.radius;
    console.log(`Circle area: ${this.area}`);
  }

  visitSquare(square: Square) {
    this.area = square.sideLength * square.sideLength;
    console.log(`Square area: ${this.area}`);
  }

  getArea(): number {
    return this.area;
  }
}

// Using the Visitor
const circle = new Circle(5);
const square = new Square(4);
const calculator = new AreaCalculator();

circle.accept(calculator); 
square.accept(calculator); 
登录后复制
登录后复制
以下代码执行完全相同的操作,但使用

反射(语言在运行时检查和操作其自身对象的能力)而不是访问者模式:

interface Shape {
  draw(): void;
}

class Circle implements Shape { 
  // ... (same as before)
  radius: number;
}

class Square implements Shape {
  // ... (same as before)
  sideLength: number;
}

function calculateArea(shape: Shape) {
  if (shape instanceof Circle) {
    const circle = shape as Circle; // Type assertion
    const area = Math.PI * circle.radius * circle.radius;
    console.log(`Circle area: ${area}`);
  } else if (shape instanceof Square) {
    const square = shape as Square; // Type assertion
    const area = square.sideLength * square.sideLength;
    console.log(`Square area: ${area}`);
  }
}

const circle = new Circle(5);
const square = new Square(4);

calculateArea(circle);
calculateArea(square);
登录后复制
登录后复制
现在是观察者模式,也在 TypeScript 中:


interface Observer {
  update(data: any): void;
}

class NewsPublisher {
  private observers: Observer[] = [];

  subscribe(observer: Observer) {
    this.observers.push(observer);
  }

  unsubscribe(observer: Observer) {
    this.observers = this.observers.filter(o => o !== observer);
  }

  notify(news:   
 string) {
    this.observers.forEach(observer => observer.update(news));
  }
}

class NewsletterSubscriber implements Observer {
  update(news: string) {
    console.log(`Received news: ${news}`);
  }
}

// Using the Observer
const publisher = new NewsPublisher();
const subscriber1 = new NewsletterSubscriber();
const subscriber2 = new NewsletterSubscriber();

publisher.subscribe(subscriber1);
publisher.subscribe(subscriber2);

publisher.notify("New product launched!");
登录后复制
登录后复制
相同,但使用内置(在 Node API 中)EventEmitter:


public class ShapeFactory {
  public Shape createShape(String type) {
    if (type.equalsIgnoreCase("CIRCLE")) {
      return new Circle();
    } else if (type.equalsIgnoreCase("SQUARE")) {
      return new Square();
    } 
    return null;   
  }
}
登录后复制
登录后复制

那时,您可能已经意识到“问题”是 OOP 实现,并且您是完全正确的,但不完全。

每种编程范式,特别是当采用最纯粹的形式时,都有其怪癖、困难或“无法直线实现的事情”,如果你愿意的话。

让我们进入函数式编程领域。您可能听说过 Monad。

无论您是否陷入数学定义的思维陷阱,我们(软件开发人员)也可以将 Monad 理解为设计模式。这是因为在纯函数的世界里,不会发生任何意外,很难想象副作用,但大多数软件产品都需要副作用,那么我们如何...?

这是 Haskell 中 IO Monad 的示例:

Map<String, Supplier<Shape>> shapeFactory = new HashMap<>();
shapeFactory.put("CIRCLE", Circle::new);
shapeFactory.put("SQUARE", Square::new);

Shape circle = shapeFactory.get("CIRCLE").get();
登录后复制
登录后复制

副作用(读取文件)包含在 IO monad 中。

让我们使用 typescript 添加一个单子示例;

interface Shape {
  draw(): void;
  accept(visitor: ShapeVisitor): void; 
}

class Circle implements Shape {
  radius: number;

  constructor(radius: number) {
    this.radius = radius;   

  }

  draw() {
    console.log("Drawing a circle");
  }

  accept(visitor: ShapeVisitor) {
    visitor.visitCircle(this); 
  }
}

class Square implements Shape {
  sideLength: number;

  constructor(sideLength: number) {
    this.sideLength = sideLength;
  }

  draw() {
    console.log("Drawing a square");
  }

  accept(visitor: ShapeVisitor) {
    visitor.visitSquare(this);
  }
}

interface ShapeVisitor {
  visitCircle(circle: Circle): void;
  visitSquare(square: Square): void;
}

class AreaCalculator implements ShapeVisitor {
  private area = 0;

  visitCircle(circle: Circle) { 
    this.area = Math.PI * circle.radius * circle.radius;
    console.log(`Circle area: ${this.area}`);
  }

  visitSquare(square: Square) {
    this.area = square.sideLength * square.sideLength;
    console.log(`Square area: ${this.area}`);
  }

  getArea(): number {
    return this.area;
  }
}

// Using the Visitor
const circle = new Circle(5);
const square = new Square(4);
const calculator = new AreaCalculator();

circle.accept(calculator); 
square.accept(calculator); 
登录后复制
登录后复制

一个经典的例子,我在互联网上见过这个 Maybe monad 大约 50 次,但它到底是什么?

它试图解决的问题

interface Shape {
  draw(): void;
}

class Circle implements Shape { 
  // ... (same as before)
  radius: number;
}

class Square implements Shape {
  // ... (same as before)
  sideLength: number;
}

function calculateArea(shape: Shape) {
  if (shape instanceof Circle) {
    const circle = shape as Circle; // Type assertion
    const area = Math.PI * circle.radius * circle.radius;
    console.log(`Circle area: ${area}`);
  } else if (shape instanceof Square) {
    const square = shape as Square; // Type assertion
    const area = square.sideLength * square.sideLength;
    console.log(`Square area: ${area}`);
  }
}

const circle = new Circle(5);
const square = new Square(4);

calculateArea(circle);
calculateArea(square);
登录后复制
登录后复制

我们忘记定义对象的属性! ?

在实际用例中,这主要是来自副作用的输入,例如从数据库或文件读取

所以现在如果我们这样做:

interface Observer {
  update(data: any): void;
}

class NewsPublisher {
  private observers: Observer[] = [];

  subscribe(observer: Observer) {
    this.observers.push(observer);
  }

  unsubscribe(observer: Observer) {
    this.observers = this.observers.filter(o => o !== observer);
  }

  notify(news:   
 string) {
    this.observers.forEach(observer => observer.update(news));
  }
}

class NewsletterSubscriber implements Observer {
  update(news: string) {
    console.log(`Received news: ${news}`);
  }
}

// Using the Observer
const publisher = new NewsPublisher();
const subscriber1 = new NewsletterSubscriber();
const subscriber2 = new NewsletterSubscriber();

publisher.subscribe(subscriber1);
publisher.subscribe(subscriber2);

publisher.notify("New product launched!");
登录后复制
登录后复制

程序爆炸了。

没有 Maybe monad 的解决方案:

import { EventEmitter } from 'events';

class NewsPublisher extends EventEmitter {
  publish(news: string) {
    this.emit('news', news);
  }
}

const publisher = new NewsPublisher();

publisher.on('news', (news) => {
  console.log(`All subscribers received the news: ${news}`);
});

publisher.publish("New product launched!");
登录后复制

程序不会爆炸。

由于可选的链接运算符,maybe monad 在 JavaScript 或 typescript 中不是必需的,但如果你使用的语言没有实现它......那么,你可以应用 Maybe monad 或我该说设计模式吗?

是的,我知道,有些人刚刚学会了 Maybe 的东西,并热切地将其立即应用到 6 个副项目中,而现在我在聚会上因为告诉你“你不需要它”而咯咯笑。不过你仍然可以使用它,事实上,如果你觉得它很酷,我邀请你这样做(在一天结束时,这是你的代码,你可以做任何你想做的事!?)


但回到基础。其他范式又如何呢?如果您跳出了 OOP/FP 的框框思考,我喜欢它!

所有范例肯定都有自己的重复出现的解决方案和技术,即使它们并不总是正式称为“设计模式”。

这里有一些例子(感谢双子座避免了我的思考,感谢我漂亮的格式和附加值?):

逻辑编程:
  • 约束逻辑编程:此范例涉及定义变量之间的约束和关系,然后让系统找到满足这些约束的解决方案。 回溯约束传播等技术对于在此范式中有效解决问题至关重要。 (在处理人工智能时非常有用)。
  • 演绎数据库:这些数据库使用逻辑规则和推理从现有数据中得出新信息。 前向/后向链接等技术是这些数据库如何运行的基础,并且可以被视为此范例中的模式。
并发编程:
  • 消息传递:在多个进程同时执行的并发系统中,消息传递是通信和协调的常用技术。 生产者-消费者读者-作者等模式提供了用于管理对资源的并发访问并确保数据一致性的既定解决方案。
  • 同步原语:这些是低级结构,例如互斥体信号量条件变量,用于控制对共享资源的访问在并发程序中。虽然不是传统意义上的“模式”,但它们代表了针对常见并发挑战的明确定义的解决方案。

面向数据的编程:

  • 数据转换管道:该范式强调通过一系列操作来转换数据。像 mapfilterreduce 这样的技术(在函数式编程中也很常见,自添加以来在 javascript 中使用得很多)是构建的基本构建块这些管道,可以被视为此范式中的模式。
  • 实体组件系统(ECS):这种架构模式在游戏开发和其他数据密集型应用程序中很流行。它涉及将实体分解为组件(数据)和系统(逻辑),促进数据局部性和高效处理。

有很多“技术”和“模式”,这个列表只是为了给你提供一些线索,如果你好奇的话。

希望您觉得本文有用,请尽快阅读!

Demystifying Design Patterns


?总结一下,给急的人!

虽然术语“设计模式”与 OOP 联系最紧密,但其他范式也有自己的一套重复出现的解决方案和技术。这些技术解决了这些范式的具体挑战和限制,为常见问题提供了既定的方法。因此,即使它们并不总是被正式标记为“设计模式”,它们也具有类似的目的,可以指导开发人员找到有效且可维护的解决方案。

我们可以将设计模式理解为众所周知的解决方法,用于修补我们正在使用的编程语言缺乏抽象的功能

这篇文章几乎完全由我撰写,指定示例由 Gemini 1.5 Pro

以上是揭秘设计模式的详细内容。更多信息请关注PHP中文网其他相关文章!

来源:dev.to
本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
作者最新文章
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板