设计模型 - 学习笔记

学习参考:
https://blog.csdn.net/m0_65346405/article/details/136994128
《系统分析师教程》
《设计模式之禅》

一. 设计模式的5大原则

1. 单一职责原则

一个类应该只有一个变化因子。

就是说,一个类要变化,比如增加功能,那么引起变化原因是单一的。不能因为这种原因,这个类要变,那种原因,这个类也要变。
比如,肯德基有早餐和快餐,就可以分成2个类(肯德基快餐店与肯德基早餐店)。这样早餐的流程调整了,不会影响快餐的操作台,物品摆放等的变化

2. 里氏替换原则

子类可以扩展父类的功能,但不能改变父类原有的功能。

父亲会做的,儿子必须要全部会做;儿子能做的,可以多于父亲。
按照里氏替换原则,定义一个鸟的类,鸟类里有飞这个动作,所以所有的鸟的子类,都需要能实现飞的动作。麻雀要能飞,鸵鸟也要能运行飞。
里氏替换原则的父类一般是接口类,子类实现接口类。

3. 依赖倒置原则

程序要依赖于抽象接口,不要依赖于具体实现。

建立对象时,类型为接口类型,这样,若修改用其他的子类建立对象,因为对象的类型是接口类型,所以相关程序都不用修改。
比如,请张三教授讲座。宣传资料里,印有张三的名字。如果张三有事不来,所有资料都作废。
如果使用技术专家代替张三教授。换成李四教授,资料仍然可以使用。

4. 迪米特原则

一个对象应该对其他对象保持最少的了解,又叫最少知道原则,尽量降低类与类之间的耦合。

UML图里,类和类之间有很多联系,尽量减少没有必要的联系。
我用洗碗机洗碗,这里3个对象:我、洗碗机和碗。由于我只操作洗碗机,我只和洗碗机产生关联。洗碗机有Add碗的动作,洗碗机和碗产生联系。

5. 开闭原则

对扩展开放,对修改关闭。

因为定义了接口,所以每次更新,可以继承接口,重新做一个新的子类,叫对扩展开放。
那么,原来的类就不要动了,叫对修改关闭。


二. 创建型模式

1. 单例模式(Singleton)

专业解释:
单例模式是一种确保在任何情况下一个类仅有一个实例,并提供全局访问点的设计模式。它主要用于控制对全局唯一资源的访问。

理解与说明:
单例模式常用一些控制类,配置类等,不存在多个实例的情况,并防止出现多个实例

在这里插入图片描述

class Singleton {
  static instance = null;

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

const instance1 = new Singleton();
const instance2 = new Singleton();

console.log(instance1 === instance2); // 输出 true,表明它们是同一个实例
2. 工厂方法(Factory Method)

专业解释:
工厂方法模式中,父类负责定义创建对象的公共接口,而子类负责生成具体的对象,这样将类的实例化操作延迟在子类中完成。

理解与说明:
比如狗、猫2类动物,并都属于动物父类,有相同的跑、叫的动作,可以通过构造器快速创建不同的动物。

在这里插入图片描述

class PetFactory {
  createPet(type) {
    switch (type) {
      case 'doc':
        return new Dog();
      case 'cat':
        return new Cat();
      default:
        throw new Error('不支持的类型');
    }
  }
}

class Dog{...}
class Cat{...}

const factory = new PetFactory ();
const pet = factory .createPet('dog');
pet.run();
pet.cry();
3. 抽象工厂(Abstract Factory)

专业解释:
抽象工厂模式提供一个接口用于创建一系列相关或相互依赖的对象,而无需指定具体类。客户端使用此接口选择所需的产品族中的产品对象。

理解与说明:
设想一个汽车工厂不仅能生产各种类型的车(如轿车、SUV等),还能生产配套的轮胎和内饰。客户只要告诉工厂要哪种类型的车,工厂就会相应地提供整套适合的汽车部件。
抽象类不仅实现的依赖倒置原则,也方便的程序规划与管理

在这里插入图片描述

// 抽象工厂
class AbstractCarFactory {
  createCar() {
    throw new Error('抽象方法,需要子类实现');
  }

  createTires() {
    throw new Error('抽象方法,需要子类实现');
  }

  createInterior() {
    throw new Error('抽象方法,需要子类实现');
  }
}

// 具体工厂
class LuxuryCarFactory extends AbstractCarFactory {
  createCar() { return new LuxuryCar(); }
  createTires() { return new PremiumTires(); }
  createInterior() { return new LeatherInterior(); }
}

// 产品类
class Car {}
class LuxuryCar extends Car {}
class Tires {}
class PremiumTires extends Tires {}
class Interior {}
class LeatherInterior extends Interior {}

// 使用
const factory = new LuxuryCarFactory();
const car = factory.createCar();
const tires = factory.createTires();
const interior = factory.createInterior();
4. 原型模式(Prototype)

专业解释:
原型模式是一种复制已有对象作为新对象的方式,通过克隆原型对象并对其稍作修改来创建新的对象,而不是重新初始化一个新对象。

理解与说明:
解决频繁创建某对象的问题,采用拷贝的方式,可以节省性能

在这里插入图片描述

function PrototypeObj(name) {
  this.name = name;
}

// 添加一个clone方法到原型上
PrototypeObj.prototype.clone = function() {
  let clone = Object.create(this);
  clone.name = this.name + '_copy';
  return clone;
};

let original = new PrototypeObj('Original');
let copy = original.clone();
console.log(copy.name); // 输出 "Original_copy"
5. 建造者模式(Builder)

专业解释:
建造者模式将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。客户端不需要知道内部组件的具体构建细节。

理解与说明:
就如同组装一台电脑,你可以选择不同配置的CPU、内存、硬盘等配件,最后由装机员按照你的配置清单来组装。组装过程(Builder)是固定的,但最终产出的电脑配置各异。

在这里插入图片描述

class ComputerBuilder {
  constructor() {
    this.computer = {
      cpu: '',
      memory: '',
      hardDrive: ''
    };
  }

  setCPU(cpu) {
    this.computer.cpu = cpu;
    return this;
  }

  setMemory(memory) {
    this.computer.memory = memory;
    return this;
  }

  setHardDrive(hardDrive) {
    this.computer.hardDrive = hardDrive;
    return this;
  }

  build() {
    return this.computer;
  }
}

class DesktopComputerBuilder extends ComputerBuilder {
  // 可能会添加一些桌面电脑特有的配置方法
}

const builder = new DesktopComputerBuilder()
  .setCPU('Intel Core i7')
  .setMemory('16GB DDR4')
  .setHardDrive('1TB SSD');

const computer = builder.build();
console.log(computer);

创建型模式总结:

单例模式 Singleton:创建的对象,只能存在一个
工厂模式 Factory Method:用一个工厂类,创建不同的对象,配合依赖倒置原则使用
抽象工厂 Abstract Factory:先把工厂的抽象规划好,在实现这个抽象,这样做有利于工厂的依赖倒置,也利于整体代码规划
原型模式 Prototype:用copy的方法建立对象,节约性能
建造者模式 Builder:先创建buider对象,并进行配置,然后再创建出我们想要的对象
以上五个名称与英文需要记住,毕竟还比较好记(软考需要)


三. 结构型模式

结构型模式描述如何将类或对象结合在一起形成更大的结构。

1. 适配器模式 (Adapter)

专业解释:
适配器模式将一个类的接口转换为客户希望的另一个接口,使原本不兼容的接口能协同工作。主要应用于当系统需要使用现有的类,但是接口不符合需求时。

理解与说明:
就像电源插头转换器,将不同标准的插头转为适应目的地插座的标准。

在这里插入图片描述

// 普通插座类
class BaseSocket {
  power() {
    console.log('插座提供电');
  }
}

// 智能插座接口
interface AdapterSocketInterface {
  request(): void;
}

// 适配器类
class AdapterSocket implements AdapterSocketInterface {
  private baseSocket: BaseSocket;

  constructor(baseSocket: BaseSocket) {
    this.baseSocket= baseSocket;
  }

  request() {
  	console.log('智能插座记录用电')
    this.baseSocket.power();
  }
}

// 使用
let baseSocket= new BaseSocket();
let adapter: AdapterSocketInterface = new AdapterSocket(baseSocket);
adapter.request(); // 输出 "执行特殊请求"
2. 桥接模式 (Bridge)

专业解释:
桥接模式将抽象部分与其实现部分分离,使它们可以独立变化。它主要用于解耦抽象和实现,从而让它们可以独立演化。

理解与说明:
比如电脑品牌和操作系统是两个维度的变化,桥接模式就是让电脑品牌可以选择不同的操作系统,二者互不影响。

在这里插入图片描述

// 抽象部分
abstract class OS {
  abstract run(user: string): void;
}

// 具体实现部分
class Win extends OS {
  run(user) {
    console.log(`${user} 运行windows操作系统`);
  }
}

class Linux extends OS {
  run(user) {
    console.log(`${user} 运行linux操作系统`);
  }

// 结构部分
class Computer{
  protected os : OS ;

  constructor(os : OS) {
    this.os = os;
  }

  setup(os : OS) {
    this.os = os;
  }

  abstract runOS(): void;
}

// 结构与实现结合
class Mycomputer extends Computer{
  constructor(os : OS) {
    super(os);
  }

  runOS() {
    this.os.run('my');
  }
}

// 使用
let computer = new Mycomputer(new Win());
computer.runOS("zw"); // 输出 "zw 运行windows操作系统"

computer.setup(new Linux());
computer.runOS('zw'); // 输出 "zw 运行linux操作系统"
3. 组合模式 (Composite)

专业解释:
组合模式允许你将对象组合成树形结构来表现“整体-部分”层次结构,并且用户对单个对象和组合对象的使用具有一致性。

理解与说明:
组合模式,做Unity与UE5的同学都不会陌生,一个对象里面,加入很多组件(Comp)

在这里插入图片描述

class Component {
  operation(): string {
    return '默认组件操作';
  }
  
  add(component: Component): void {}
  remove(component: Component): void {}
}

class Leaf extends Component {
  operation(): string {
    return '叶子节点操作';
  }
}

class Composite extends Component {
  private children: Component[] = [];

  add(component: Component): void {
    this.children.push(component);
  }

  remove(component: Component): void {
    const index = this.children.indexOf(component);
    if (index > -1) {
      this.children.splice(index, 1);
    }
  }

  operation(): string {
    let result = '';
    for (const child of this.children) {
      result += child.operation();
    }
    return `复合组件操作: ${result}`;
  }
}

// 使用
let leaf = new Leaf();
console.log(leaf.operation()); // 输出:"叶子节点操作"

let composite = new Composite();
composite.add(leaf);

console.log(composite.operation()); // 输出:"复合组件操作: 叶子节点操作"
4. 装饰模式 (Decorator)

专业解释:
装饰模式动态地给一个对象添加一些额外的职责,提供比继承更有弹性的替代方案来扩展对象的功能。

理解与说明:
比如咖啡基础款可以加糖、加奶、加香草等,每一种装饰都是在原有基础上增加新特性,而不是每次都创建新的咖啡品种。

在这里插入图片描述

// 基础组件
class Coffee {
  cost(): number {
    return 10;
  }

  description(): string {
    return 'Coffee';
  }
}

// 装饰者
abstract class CoffeeDecorator implements Coffee {
  protected coffee: Coffee;

  constructor(coffee: Coffee) {
    this.coffee = coffee;
  }

  cost(): number {
    return this.coffee.cost();
  }

  description(): string {
    return this.coffee.description();
  }
}
// 实现
class MilkCoffee extends CoffeeDecorator {
  constructor(coffee: Coffee) {
    super(coffee);
  }

  cost(): number {
    return super.cost() + 2; // 加入牛奶的成本
  }

  description(): string {
    return super.description() + ', Milk'; // 描述中加入牛奶
  }
}

// 使用
let coffee = new Coffee();
console.log(coffee.cost()); // 输出:10
console.log(coffee.description()); // 输出:"Coffee"

let milkCoffee = new MilkCoffee(coffee);
console.log(milkCoffee.cost()); // 输出:12
console.log(milkCoffee.description()); // 输出:"Coffee, Milk"
阶段总结
  1. 装饰模式(Decorator)与适配模式(Adapter)的差别:
    装饰模式的接口是不变的,适配模式目的就是扩展接口
  2. 装饰模式(Decorator)与组合模式(Composite)的差别:
    组合模式是在主类里组合子类,装饰模式是装饰类继承基础的类
  3. 组合模式(Composite)与桥接模式(Bridge)的差别:
    桥接模式用一个函数改变对象,组合模式是有多个子对象
5. 外观模式 (Facade)

专业解释:
外观模式为子系统中的一组接口提供一个统一的高层接口,简化了该子系统的使用。

理解与说明:
MediaFacade作为外观类,为客户端提供了startMovie和endMovie两个简单的方法,分别用于开始播放电影(包括视频、音频和字幕)和结束播放(停止视频、音频并隐藏字幕)。客户端不再需要直接与AudioPlayer、VideoPlayer和SubtitleController这些子系统交互,从而降低了代码的复杂度和耦合度。

在这里插入图片描述

class AudioPlayer {
  playAudio() {
    console.log("Playing audio...");
  }
  stopAudio() {
    console.log("Stopping audio...");
  }
}

class VideoPlayer {
  playVideo() {
    console.log("Playing video...");
  }
  stopVideo() {
    console.log("Stopping video...");
  }
}

class SubtitleController {
  showSubtitle() {
    console.log("Showing subtitles...");
  }
  hideSubtitle() {
    console.log("Hiding subtitles...");
  }
}

// 外观类
class MediaFacade {
  constructor() {
    this.audioPlayer = new AudioPlayer();
    this.videoPlayer = new VideoPlayer();
    this.subtitleController = new SubtitleController();
  }

  startMovie() {
    this.videoPlayer.playVideo();
    this.audioPlayer.playAudio();
    this.subtitleController.showSubtitle();
  }

  endMovie() {
    this.audioPlayer.stopAudio();
    this.videoPlayer.stopVideo();
    this.subtitleController.hideSubtitle();
  }
}

// 客户端代码
const media = new MediaFacade();
media.startMovie(); // 自动播放视频、音频并显示字幕
media.endMovie();   // 停止播放并隐藏字幕
6. 享元模式 (Flyweight)

专业解释:
享元模式运用共享技术有效支持大量细粒度的对象,通过共享已存在的同类对象来大幅度减少创建新对象的数量,从而节省系统资源。

理解与说明:
例如一个大型游戏中大量的士兵角色,可以通过共享一部分数据来减少内存占用。
比如士兵说话,同样类别的士兵,他们说话是一样的,都是电脑发出同样的声音,就可以做进享元。

在这里插入图片描述

// 享元工厂类
function FlyweightFactory() {
  this.flyweights = {};
}
 
FlyweightFactory.prototype.getFlyweight = function(key) {
  if (!this.flyweights[key]) {
    this.flyweights[key] = new ConcreteFlyweight(key);
  }
  return this.flyweights[key];
};
 
// 具体享元类
function ConcreteFlyweight(key) {
  this.key = key;
}
 
ConcreteFlyweight.prototype.operation = function(extrinsicState) {
  return `${this.key} - ${extrinsicState}`;
};
 
// 客户端代码
var factory = new FlyweightFactory();
var f1 = factory.getFlyweight("A");
var f2 = factory.getFlyweight("B");
var f3 = factory.getFlyweight("A");
 
console.log(f1.operation("data1")); // A - data1
console.log(f2.operation("data2")); // B - data2
console.log(f3.operation("data3")); // A - data3
 
// 此时,内部的flyweights对象只创建了两个对象,一个是key为'A'的,一个是key为'B'的,
// 因此可以看到f1和f3实际上是指向同一个对象的引用。
7. 代理模式 (Proxy)

专业解释:
代理模式为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不能或者不应该直接引用另一个对象,代理对象作为中间人起到中介作用。

理解与说明:
类似于明星经纪人,粉丝们通常不会直接接触明星,而是通过经纪人进行沟通和安排事务。

在这里插入图片描述

class RealSubject {
  request(): string {
    return '真实的请求响应';
  }
}

class Proxy {
  private realSubject: RealSubject;

  constructor(realSubject: RealSubject) {
    this.realSubject = realSubject;
  }

  request(): string {
    if (this.checkAccess()) {
      return this.realSubject.request();
    } else {
      throw new Error('无权访问');
    }
  }

  private checkAccess(): boolean {
    // 这里模拟检查权限的过程
    return true; // 假设当前有访问权限
  }
}

// 使用
let realSubject = new RealSubject();
let proxy = new Proxy(realSubject);

try {
  console.log(proxy.request()); // 输出:"真实的请求响应"
} catch (error) {
  console.error(error.message);
}
结构型模式总结

7种结构模式,
适配器模式 (Adapter),扩展了插座的接口
桥接模式 (Bridge),setup一各新的内核对象
组合模式 (Composite),主对象,组合各类子对象
装饰模式 (Decorator),保持接口不变,但是改变了功能,继承的一种替代方案
外观模式 (Facade),为几个对象,统一到一个类里,提供执行函数
享元模式 (Flyweight),把重复的对象,独立成享元,无需重复创建
代理模式(Proxy),为某对象,进行功能代理,比如权限限制等

结构型模式是把对象通过类,结合在一起使用,Decorator装饰英文需要记忆下,其他英文相对好记
记住了结构型7类和创建型5类,其他都是行为型模式,一般软考选择题就没有问题了


四. 行为型模式

行为型模式是对在不同的对象之间划分责任和算法的抽象化,它不仅仅是关于类和对象的,而且是关于它们之间的相互作用的。

1. 职责链模式(Chain of Responsibility)

专业解释:
使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递请求,直到有一个对象处理它为止。

理解与说明:
就像公司的请假审批流程,员工提交请假申请后,申请会按照经理、总监、总经理的顺序逐级审批,直到某一级别负责人批准或拒绝为止。
通过next的设计,一层层的上报处理

在这里插入图片描述

// 抽象的处理器类
class Handler {
  constructor(next) {
    this.next = next;
  }
 
  handleRequest(request) {
    if (this.next) {
      return this.next.handleRequest(request);
    }
  }
}
 
// 具体的处理器A
class ConcreteHandlerA extends Handler {
  handleRequest(request) {
    if (request === 'A') {
      console.log('请求被处理器A处理了');
    } else {
      return super.handleRequest(request);
    }
  }
}
 
// 具体的处理器B
class ConcreteHandlerB extends Handler {
  handleRequest(request) {
    if (request === 'B') {
      console.log('请求被处理器B处理了');
    } else {
      return super.handleRequest(request);
    }
  }
}
 
// 使用
const handlerA = new ConcreteHandlerA(null);
const handlerB = new ConcreteHandlerB(handlerA);
 
handlerB.handleRequest('A'); // 输出: 请求被处理器A处理了
handlerB.handleRequest('B'); // 输出: 请求被处理器B处理了
handlerB.handleRequest('C'); // 不在handlerA和handlerB的处理范围内,不输出任何信息
2. 命令模式(Command)

专业解释:
将一个请求封装为一个对象,使得可以用不同的请求对客户进行参数化;对请求排队或者记录请求日志,以及支持可撤销的操作。

理解与说明:
如同遥控器上的按键,每一个按键代表一个命令,按下按键就能执行相应的操作,还能实现撤销操作等功能。

在这里插入图片描述

class Receiver {
  executeCommand() {
    console.log('接收者执行命令');
  }
}

class Command {
  constructor(receiver: Receiver) {
    this.receiver = receiver;
  }

  execute() {
    this.receiver.executeCommand();
  }

  undo() {
    console.log('撤销命令');
  }
}

class Invoker {
  command: Command | null = null;

  setCommand(command: Command) {
    this.command = command;
  }

  invoke() {
    if (this.command) {
      this.command.execute();
    }
  }

  undoInvoke() {
    if (this.command) {
      this.command.undo();
    }
  }
}

let receiver = new Receiver();
let command = new Command(receiver);
let invoker = new Invoker();
invoker.setCommand(command);
invoker.invoke(); // 输出 "接收者执行命令"
invoker.undoInvoke(); // 输出 "撤销命令"
3. 解释器模式(Interpreter)

专业解释:
给定一门语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。

理解与说明:
就像是编程语言的编译器或解释器,它解析程序员写的代码并执行相应操作。

在这里插入图片描述

class Expression {
  interpret(context) {
    throw new Error('Subclasses must implement interpret().');
  }
}

class TerminalExpression extends Expression {
  interpret(context) {
    // 根据具体上下文解释终结符表达式
  }
}

class NonTerminalExpression extends Expression {
  interpret(context) {
    // 根据具体上下文解释非终结符表达式,可能包含子表达式的解释
  }
}

// 上下文对象
class Context {}

// 使用解释器
let context = new Context();
let expression = new TerminalExpression(); // 或 NonTerminalExpression
expression.interpret(context);
4. 迭代器模式(Iterator)

专业解释:
提供一种方法顺序访问聚合对象的各个元素,而又不暴露其底层表示。迭代器模式定义了一个访问一系列元素的接口,各元素之间关系紧密但又不需要暴露细节。

理解与说明:
在Js里,通过使用 Symbol.iterator的迭代器的语法,Collection类可以使用for (let item of collection)的方式,依次获得内容的方法;其他语言也有用while(obj.hasNext())等方法,去实现迭代器的循环获取值的功能。

在这里插入图片描述

class Collection {
  constructor(items = []) {
    this.items = items;
  }

  [Symbol.iterator]() {
    let index = 0;
    let collection = this.items;
    return {
      next: () => {
        if (index < collection.length) {
          return { value: collection[index++], done: false };
        } else {
          return { done: true };
        }
      }
    };
  }
}

let collection = new Collection(['Apple', 'Banana', 'Cherry']);
for (let item of collection) {
  console.log(item); // 输出 "Apple", "Banana", "Cherry"
}
5. 中介者模式(Mediator)

专业解释:
定义一个中介对象来封装一系列的对象交互,使各对象不需要显式地相互引用,从而降低耦合度,同时使得系统易于扩展。

理解与说明:
就像公司内部员工有问题不直接相互联系,而是通过人事部门作为中介进行协调,这样避免了员工间的直接依赖关系。

在这里插入图片描述

class Mediator { //中介
  constructor() {
    this.colleagues = {}; //同事
  }

  register(name, colleague) {
    this.colleagues[name] = colleague;
    colleague.setMediator(this);
  }

  send(message, sender) {
    for (const key in this.colleagues) {
      if (key !== sender) {
        this.colleagues[key].receive(message);
      }
    }
  }
}

class Colleague {
  constructor(name) {
    this.name = name;
    this.mediator = null;
  }

  setMediator(mediator) {
    this.mediator = mediator;
  }

  receive(message) {
    console.log(`${this.name} received message from mediator: ${message}`);
  }

  sendMessage(message) {
    this.mediator.send(message, this.name);
  }
}

let mediator = new Mediator();
let colleague1 = new Colleague('Colleague1');
let colleague2 = new Colleague('Colleague2');

mediator.register('Colleague1', colleague1);
mediator.register('Colleague2', colleague2);

colleague1.sendMessage('Hello from Colleague1'); // 输出 "Colleague2 received message from mediator: Hello from Colleague1"
6. 备忘录模式(Memento)

专业解释:
在不破坏封装性的前提下,捕获一个对象的内部状态以便稍后恢复。这种模式主要用于数据备份和还原操作,防止外部对象随意修改内部状态。

理解与说明:
就像游戏存档,你可以随时保存游戏进度并在任何时候恢复到之前的状态。

在这里插入图片描述

class Originator {
  constructor(state) {
    this.state = state;
  }

  getState() {
    return this.state;
  }

  setState(state) {
    this.state = state;
  }

  createMemento() {
    return { state: this.getState() }; // 创建备忘录对象
  }

  restoreFromMemento(memento) {
    this.setState(memento.state); // 从备忘录恢复状态
  }
}

class Caretaker {
  constructor() {
    this.mementos = [];
  }

  addMemento(memento) {
    this.mementos.push(memento);
  }

  getMemento(index) {
    return this.mementos[index];
  }
}

let originator = new Originator('Initial State');
let caretaker = new Caretaker();

caretaker.addMemento(originator.createMemento()); // 存档
originator.setState('New State');

console.log(originator.getState()); // 输出 "New State"

originator.restoreFromMemento(caretaker.getMemento(0)); 
7. 观察者模式(Observer)

专业解释:
定义了对象之间的依赖关系,一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。

理解与说明:
像订阅新闻一样,当你订阅了某个主题后,每当有新的新闻更新时,你就会收到通知。

在这里插入图片描述

class Subject {
  constructor() {
    this.observers = [];
  }

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

  unsubscribe(observer) {
    const index = this.observers.indexOf(observer);
    if (index !== -1) {
      this.observers.splice(index, 1);
    }
  }

  notify(data) {
    this.observers.forEach((observer) => observer.update(data));
  }
}

class Observer {
  update(data) {
    console.log('Received data:', data);
  }
}

let subject = new Subject();
let observer1 = new Observer();
let observer2 = new Observer();

subject.subscribe(observer1);
subject.subscribe(observer2);

subject.notify('New Data'); // 输出两次 "Received data: New Data"

subject.unsubscribe(observer1);
subject.notify('Another Data'); // 输出一次 "Received data: Another Data"
8. 状态模式(State)

专业解释:
允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。

理解与说明:
就像交通信号灯,红绿黄三种状态决定了不同的行为表现。

在这里插入图片描述

// 状态接口
class State {
    performAction(context) {
        throw new Error('子类必须实现这个方法');
    }
}
 
// 实现状态接口的具体状态类
class ConcreteStateA extends State {
    performAction(context) {
        console.log('执行状态 A 的动作');
        // 可以根据需要修改状态
        context.setState(new ConcreteStateB());
    }
}
 
// 实现状态接口的具体状态类
class ConcreteStateB extends State {
    performAction(context) {
        console.log('执行状态 B 的动作');
        // 可以根据需要修改状态
        context.setState(new ConcreteStateA());
    }
}
 
// 环境类
class Context {
    constructor(state) {
        this._state = state;
    }
 
    setState(state) {
        this._state = state;
    }
 
    request() {
        this._state.performAction(this);
    }
}
 
// 使用示例
const context = new Context(new ConcreteStateA());
context.request(); // 输出: 执行状态 A 的动作
context.request(); // 输出: 执行状态 B 的动作
9. 策略模式(Strategy)

专业解释:
定义一系列算法,把它们一个个封装起来,并且使它们可以相互替换。策略模式让算法的变化独立于使用算法的客户。

理解与说明:
如同不同的折扣计算策略,可以灵活切换,不影响使用折扣策略的购物车系统。

在这里插入图片描述

class Strategy {
  calculatePrice(price) {
    throw new Error('Subclasses must implement calculatePrice().');
  }
}

class NormalStrategy extends Strategy {
  calculatePrice(price) {
    return price;
  }
}

class DiscountStrategy extends Strategy {
  constructor(discountRate) {
    super();
    this.discountRate = discountRate;
  }

  calculatePrice(price) {
    return price * (1 - this.discountRate);
  }
}

class ShoppingCart {
  constructor(strategy) {
    this.strategy = strategy;
  }

  setStrategy(strategy) {
    this.strategy = strategy;
  }

  calculateTotalPrice(items) {
    let totalPrice = 0;
    for (const item of items) {
      totalPrice += this.strategy.calculatePrice(item.price);
    }
    return totalPrice;
  }
}

let normalShoppingCart = new ShoppingCart(new NormalStrategy());
let discountedShoppingCart = new ShoppingCart(new DiscountStrategy(0.1));

let items = [{price: 100}, {price: 200}, {price: 300}];
console.log(normalShoppingCart.calculateTotalPrice(items)); // 输出 600

discountedShoppingCart.setStrategy(new DiscountStrategy(0.2));
console.log(discountedShoppingCart.calculateTotalPrice(items)); // 输出 480
10. 模板方法模式(Template Method)

专业解释:
在抽象类中定义一个基本算法的框架,而将一些步骤延迟到子类中实现。它允许子类在不修改整体算法结构的情况下重新定义某些步骤。

理解与说明:
就像烹饪菜谱,给出了做一道菜的基本流程,但具体每个步骤的实现(如炒菜调料的选择)由各个具体的菜品子类决定。

在这里插入图片描述

class AbstractClass {
  templateMethod() {
    this.baseOperation1();
    this.optionalOperation1(); // 子类可覆盖此方法
    this.requiredOperation();
    this.optionalOperation2(); // 子类可覆盖此方法
  }

  baseOperation1() {
    console.log('基本操作1');
  }

  requiredOperation() {
    console.log('必须执行的操作');
  }

  optionalOperation1() { /* 子类可覆盖 */ }
  optionalOperation2() { /* 子类可覆盖 */ }
}

class ConcreteClass extends AbstractClass {
  optionalOperation1() {
    console.log('具体类实现的操作1');
  }

  optionalOperation2() {
    console.log('具体类实现的操作2');
  }
}

let concrete = new ConcreteClass();
concrete.templateMethod();
11. 访问者模式(Visitor)

专业解释:
封装一些作用于某种数据结构中的各种元素的操作,它可以在不改变元素类的前提下定义作用于这些元素的新操作。

理解与说明:
类似检查员去多个部门审核,各部门只需提供接受检查的接口,而无需关心检查的具体规则,检查员则携带不同规则去访问各部门。

在这里插入图片描述

// 定义元素接口
class Element {
  accept(visitor) {
    throw new Error('This method should be implemented in subclasses');
  }
}

// 具体元素类
class ConcreteElementA extends Element {
  accept(visitor) {
    visitor.visitConcreteElementA(this);
  }
}

class ConcreteElementB extends Element {
  accept(visitor) {
    visitor.visitConcreteElementB(this);
  }
}

// 定义访问者接口
interface Visitor {
  visitConcreteElementA(element);
  visitConcreteElementB(element);
}

// 具体访问者类
class ConcreteVisitor implements Visitor {
  visitConcreteElementA(element) {
    console.log('Visited ConcreteElementA');
  }

  visitConcreteElementB(element) {
    console.log('Visited ConcreteElementB');
  }
}

let elementA = new ConcreteElementA();
let elementB = new ConcreteElementB();
let visitor = new ConcreteVisitor();

elementA.accept(visitor); // 输出 "Visited ConcreteElementA"
elementB.accept(visitor); // 输出 "Visited ConcreteElementB"
行为型模式总结

模板方法模式(Template Method),编写一套顺序运行的方法,其中一些方法,由子类来继承实现。
责任链模式(Chain of Responsibility),一个对象套一个对象,根据一个判断值,从最外层开始判断是否能处理
命令模式(Command),把一个处理,封装成一个命令的对象,然后让一个触发器,来触发这个命令
状态模式(State),把不同状态写成对象,状态的变化是固定,写在对象里,开放统一处理函数
以上4个模式中英文都好记,考试的时候,容易归类到行为型模式

备忘录模式(Memento),做一个数组,记录所有的关键状态数据,可以返回这些状态的值
策略模式(Strategy),定义不同的算法类,把算法给到一个执行类,生成不同的算法执行对象(策略对象),由这些策略对象来处理实际业务,比如不同打折的卡来消费
以上2个模型,若是中文,很清楚是行为型,英文难记些

解释器(Interpreter),开发不同的解释器,来解释不同的内容对象
迭代器(Iterator),编写一个具有foreach功能的方法,来循环得到所有参数或值,一般放在for,while里迭代
以上2个器比较特殊,容易归类到行为型,以i开头的,er/or结尾的就是这2个器,英文看比较眼熟

中介者 (Mediator),对象需要都注册在中介者上,当对象要发送信息时,都发给中介者,中介者除了对象自己,群发给其他对象。
观察者(Observer),一个主题,会被很多对象订阅或观察,当这个主题对象,发送一个信息时,订阅的对象,都可以收到信息
2者相同的是,不同的对象,绑定在一个主题或中介上;区别是,中介者模式是对象发信息,中介转发;观察者模式是主题发信息,绑定的对象接收
访问者(Visitor),访问者需要定义所有的情况下的处理方法,然后不同的情况类,接收访问者,并调用访问者应执行什么处理方法。这里把具体处理给予访问者,把如何处理拆出,给到情况类。

以上3个行为型的者+2个器,和建造者(Builder)、适配器(Adapter)、装饰器(Decorator),总共有er,or 8个,要区分好,这样基本软考考到这部分的题目,应该不会选错。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/770791.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Elasticsearch 8.x 存储有无压缩?能压缩到多少?

1、认知前提 Elasticsearch 支持压缩&#xff0c;压缩方式默认为&#xff1a;LZ4 压缩算法。 具体参见&#xff1a; The default value compresses stored data with LZ4 compression, but this can be set to best_compression which uses DEFLATE for a higher compression r…

mybatis mapper.xml 比较运算符(大于|小于|等于)的写法: 转义和<![CDATA[]]>

文章目录 引言I 使用xml 原生转义的方式进行转义II 使用 <![CDATA[ 内容 ]]>引言 应用场景:查询时间范围 背景:在 *.xml 中使用常规的 < > = <= >= 会与xml的语法存在冲突 <![CDATA[]]> 比 转义符 来的繁琐 <![CDATA[]]> 表示xml解析器忽略…

Linux内网端口转公网端口映射

由于服务商做安全演练&#xff0c;把原先服务器内网的端口映射到外网端口全都关闭了&#xff0c;每次维护服务器特别麻烦&#xff0c;像数据库查询如果用原生的mysql 去连接&#xff0c;查询返回的结果乱了&#xff0c;非常不方便。 查了服务还是可以正常访问部分外网的&#x…

mongodb在windows环境安装部署

一、mongodb 1.释义 MongoDB 是一种开源的文档型 NoSQL 数据库管理系统&#xff0c;使用 C 编写&#xff0c;旨在实现高性能、高可靠性和易扩展性。MongoDB 采用了面向文档的数据模型&#xff0c;数据以 JSON 风格的 BSON&#xff08;Binary JSON&#xff09;文档存储&#x…

Unity射击游戏开发教程:(29)躲避敌人的子弹射击

在这篇文章中,我将介绍如何创建一个可以使玩家火力无效的敌人。创建的行为如下...... 当玩家向敌人开火时,敌人会向左或向右移动。向左或向右的移动是随机选择的,并在一段时间后停止敌人的移动。如果敌人移出屏幕,它就会绕到另一边。将一个精灵拖到画布上,将其缩小以匹配游…

mdb转gdb实现过程介绍(2)三种方式实现GDB数据库的读、写,并将实现方式与ArcGIS环境共存配置

一、内容提示 通过解析mdb地理数据库&#xff0c;获取了图层之间的组织结构、空间参考、表字段属性等信息。 下一步&#xff0c;就是将数据输出到GDB中。 下面详细介绍python3.9版本&#xff0c;读写GDB数据的方法&#xff1a; &#xff08;1&#xff09;使用ArcPy创建GDB、读写…

【MySQL基础篇】函数及约束

1、函数 函数是指一段可以直接被另一段程序程序调用的程序或代码。 函数 - 字符串函数 MySQL中内置了很多字符串函数&#xff0c;常用的几个如下&#xff1a; 函数功能CONCAT(S1,S2,...,Sn)字符串拼接&#xff0c;将S1,S2,...,Sn拼接成一个字符串LOWER(str)将字符串str全部…

STM32第十二课:ADC检测烟雾浓度(MQ2)

文章目录 需求一、MQ-2 气体传感器特点应用电路及引脚 二、实现流程1.开时钟&#xff0c;分频&#xff0c;配IO2.配置ADC的工作模式3.配置通道4.复位&#xff0c;AD校准5.数值的获取 需求实现总结 需求 使用ADC将MQ2模块检测到的烟雾浓度模拟量转化为数字量。 最后&#xff0c…

【ElementPlus源码】Button 按钮

文章目录 准备工作属性方法模板使用到的hooksuse-propuseDeprecated 看源码时候做的笔记。 准备工作 本地开发 | Element Plus (element-plus.org) 文档与源码对应&#xff1a;docs/examples/button 组件源码位置&#xff1a; packages/components/button/src 属性 定义在 …

C++面试宝典30题丨第一题:开灯

专栏导读 见得题目越多&#xff0c;考试时抽中的概率也越大。每一题都有详细的解答思路和独有的视频讲解。 本文收录于&#xff1a;C面试宝典&#xff08;送视频讲解&#xff09; ☆☆☆购买专栏后&#xff0c;请加微信会私发讲解视频&#xff01; 题目描述 一条名叫Mango的街…

图书管理系统(持久化存储数据以及增添新功能)

目录 一、数据库表设计 二、引入MyBatis 和MySQL 驱动依赖 三、配置数据库 & 日志 四、Model创建 五、枚举类 常量类用户登录 六、用户登录 七、添加图书 八、图书列表 九、修改图书 十、删除图书 十一、批量删除 十二、强制登录 十三、前端代码 &#xff0…

使用 HBuilder X 进行 uniapp 小程序开发遇到的问题合集

文章目录 背景介绍问题集锦1. 在 HBuilderX 点击浏览器运行时&#xff0c;报 uni-app vue3编译器下载失败 安装错误2.在 HBuilderX 点击微信小程序运行时&#xff0c;报 微信开发者工具打开项目失败&#xff0c;请参阅启动日志错误 背景介绍 HBuilder X 版本&#xff1a;HBui…

餐饮界的新传奇:沃可趣员工社区,让品牌关怀在指尖流淌

咖啡师与顾客发生肢体冲突、员工用咖啡粉泼顾客……某精品咖啡一天爆出两个大瓜&#xff01; 很快有网友指出咖啡店员工长期遭受重压&#xff0c;与品牌之间存在根本矛盾。 同样做餐饮的老牌快餐&#xff0c;门店密度与之不相上下&#xff0c;却很少发生这样的暴雷。 不仅因…

六.核心动画 - 特殊图层①

引言 本专栏到目前为止已经介绍了CALayer&#xff0c;了解了它的绘画和动画功能。但是Core Animation图层不仅仅能够用于图片和颜色&#xff0c;本篇博客就来介绍一下一些CALayer的子类特殊图层&#xff0c;来进一步扩展Core Animation的绘图能力。 特殊图层 Core Animation…

Vue实现金钱输入框组件自动带千位逗号

新建PriceInput.vue <template><div id"bord"><el-inputv-model"inputValue"v-bind"$attrs":maxlength"maxlength"input"handleInput"focus"handleFocus"blur"handleBlur"change"h…

自闭症儿童:探索症状背后的多彩内心世界

在星启帆自闭症康复中心&#xff0c;我们每天与一群独特而珍贵的孩子相遇——他们&#xff0c;是自闭症谱系障碍的患儿。自闭症&#xff0c;这一复杂的神经发育障碍&#xff0c;以其多样化的症状表现&#xff0c;为每个孩子的生活轨迹绘上了不同的色彩。 自闭症孩子的症状各异…

Websocket通信实战项目(js)(图片互传应用)(下)客户端H5+css+js实现

Rqtz : 个人主页 ​ 共享IT之美&#xff0c;共创机器未来 Sharing the Beauty of IT and Creating the Future of Machines Together 目录 起始 客户端GUI Javascripts连接websocket 使用localStorage保存用户输入的IP Websocket连接成功 Websocket接收数据 解析…

【Linux】正确的关机方法

1. Linux正确的关机方式 如何关机呢&#xff1f;我想&#xff0c;很多朋友在DOS年代已经有在玩计算机了。在当时我们关闭DOS的系统时&#xff0c;常常是直接关闭电源开关&#xff0c;而Windows 在你不爽的时候&#xff0c;按着电源开关四秒也可以关机&#xff0c;但是在Linux则…

旧衣回收小程序:减少资源浪费,提高回收效率

当下&#xff0c;旧衣服回收成为了大众热衷的事&#xff0c;不少居民都会把闲置的衣物进行回收&#xff0c;旧衣回收行业逐渐火爆。不过&#xff0c;传统的旧衣回收模式已经不符合当下时代发展&#xff0c;具有较大的不便利性。 因此&#xff0c;为解决这一问题&#xff0c;线…

PG实践|内置函数之GENERATE_SERIES之深入理解(二)

&#x1f4eb; 作者简介&#xff1a;「六月暴雪飞梨花」&#xff0c;专注于研究Java&#xff0c;就职于科技型公司后端工程师 &#x1f3c6; 近期荣誉&#xff1a;华为云云享专家、阿里云专家博主、腾讯云优秀创作者、ACDU成员 &#x1f525; 三连支持&#xff1a;欢迎 ❤️关注…