第3期 - 沙漠绿洲

封面图来源于阿拉善沙漠🏜️绿洲,史上最开心的一次旅行,必须要记录下。

技术分享-设计模式相关

Visitor访问者模式

原理

每个元素都有一个 accept 方法,接收一个访问者作为参数。访问者类实现了对每种元素类型的访问方法。当访问者访问元素时,元素会调用访问者的对应方法,并将自身传递给该方法。

主要组成部分

Element(元素):这是对象结构中的接口或抽象类,定义了一个 accept 方法,这个方法接收一个访问者对象作为参数。
ConcreteElement(具体元素):实现了 Element 接口的类。它们在实现的 accept 方法中调用访问者的相应方法,将自身 (this) 传递给访问者。
Visitor(访问者):定义了一个接口或抽象类,包含访问所有具体元素的方法,通常是 visitConcreteElementA、visitConcreteElementB 等。
ConcreteVisitor(具体访问者):实现了 Visitor 接口的类。每个具体访问者类都实现了访问不同元素的具体操作。

定义元素接口和元素类

// Element 接口
class Element {
  accept(visitor) {
    throw new Error('This method must be overridden!');
  }
}
// 元素类:FunctionElement
class FunctionElement extends Element {
  operation() {
    return 'Function Operation';
  }
  accept(visitor) {
    visitor.visitFunctionElement(this);
  }
}
// 元素类:ObjectElement
class ObjectElement extends Element {
  operation() {
    return 'Object Operation';
  }
  accept(visitor) {
    visitor.visitObjectElement(this);
  }
}
// 元素类:StringElement
class StringElement extends Element {
  operation() {
    return 'String Operation';
  }
  accept(visitor) {
    visitor.visitStringElement(this);
  }
}

定义访问者接口和具体访问者类

// Visitor 接口
class Visitor {
  visitFunctionElement(functionElement) {
    throw new Error('This method must be overridden!');
  }

  visitObjectElement(objectElement) {
    throw new Error('This method must be overridden!');
  }

  visitStringElement(stringElement) {
    throw new Error('This method must be overridden!');
  }
}

// 具体访问者类:ConcreteVisitor1
class ConcreteVisitor1 extends Visitor {
  visitFunctionElement(functionElement) {
    console.log(`ConcreteVisitor1 logs ${functionElement.operation()}`);
  }

  visitObjectElement(objectElement) {
    console.log(`ConcreteVisitor1 logs ${objectElement.operation()}`);
  }

  visitStringElement(stringElement) {
    console.log(`ConcreteVisitor1 logs ${stringElement.operation()}`);
  }
}

// 具体访问者类:ConcreteVisitor2
class ConcreteVisitor2 extends Visitor {
  visitFunctionElement(functionElement) {
    console.log(`ConcreteVisitor2 processes ${functionElement.operation()}`);
  }

  visitObjectElement(objectElement) {
    console.log(`ConcreteVisitor2 processes ${objectElement.operation()}`);
  }

  visitStringElement(stringElement) {
    console.log(`ConcreteVisitor2 processes ${stringElement.operation()}`);
  }
}

使用访问者模式

// 创建具体元素对象
const elements = [
  new FunctionElement(),
  new ObjectElement(),
  new StringElement()
];

// 创建具体访问者对象
const visitor1 = new ConcreteVisitor1();
const visitor2 = new ConcreteVisitor2();

// 使用访问者模式
elements.forEach(element => {
  element.accept(visitor1); // 使用访问者1
  element.accept(visitor2); // 使用访问者2
});

完整的实现

// 定义元素接口
class Element {
  accept(visitor) {
    throw new Error("This method must be overridden!");
  }
}

// 具体元素 - 函数元素
class ConcreteFunctionElement extends Element {
  accept(visitor) {
    visitor.visitConcreteFunctionElement(this);
  }

  operation() {
    return 'Function Element';
  }
}

// 具体元素 - 对象元素
class ConcreteObjectElement extends Element {
  accept(visitor) {
    visitor.visitConcreteObjectElement(this);
  }

  operation() {
    return 'Object Element';
  }
}

// 具体元素 - 字符串元素
class ConcreteStringElement extends Element {
  accept(visitor) {
    visitor.visitConcreteStringElement(this);
  }

  operation() {
    return 'String Element';
  }
}

// 访问者接口
class Visitor {
  visitConcreteFunctionElement(element) {
    throw new Error("This method must be overridden!");
  }

  visitConcreteObjectElement(element) {
    throw new Error("This method must be overridden!");
  }

  visitConcreteStringElement(element) {
    throw new Error("This method must be overridden!");
  }
}

// 具体访问者1
class ConcreteVisitor1 extends Visitor {
  visitConcreteFunctionElement(element) {
    console.log(`ConcreteVisitor1 logs ${element.operation()}`);
  }

  visitConcreteObjectElement(element) {
    console.log(`ConcreteVisitor1 logs ${element.operation()}`);
  }

  visitConcreteStringElement(element) {
    console.log(`ConcreteVisitor1 logs ${element.operation()}`);
  }
}

// 具体访问者2
class ConcreteVisitor2 extends Visitor {
  visitConcreteFunctionElement(element) {
    console.log(`ConcreteVisitor2 processes ${element.operation()}`);
  }

  visitConcreteObjectElement(element) {
    console.log(`ConcreteVisitor2 processes ${element.operation()}`);
  }

  visitConcreteStringElement(element) {
    console.log(`ConcreteVisitor2 processes ${element.operation()}`);
  }
}

// 使用访问者模式
const elements = [
  new ConcreteFunctionElement(),
  new ConcreteObjectElement(),
  new ConcreteStringElement()
];

const visitor1 = new ConcreteVisitor1();
const visitor2 = new ConcreteVisitor2();

elements.forEach(element => {
  element.accept(visitor1);
  element.accept(visitor2);
});

Q:在 Visitor 模式中,为什么每个访问者类(如 ConcreteVisitor1 和 ConcreteVisitor2)都需要为所有的元素类型(如函数、对象、字符串等)定义具体的处理方法
A:访问者模式的核心思想是将操作的逻辑和元素对象的结构解耦,从而可以在不修改元素类的情况下扩展操作

Q:为什么元素按类型定义,而每个访问者需要处理所有类型的元素
A: 每个元素类(ConcreteElement)表示一种特定的结构或类型。将所有可能的操作集中在访问者中。通过这种方式,你可以在一个地方定义对所有元素类型的操作,而无需修改元素类本身。

发布订阅

原理

订阅者把自己想订阅的事件处理函数注册到统一的调度中心中,当发布者向调度中心发布数据时,由调度中心统一调用订阅者注册到调度中心的事件处理函数。 当一个对象的状态发生改变时,所有依赖它的对象都将得到通知并执行相应操作。

调度者:管理事件和通知的中介 发布者:emit(eventName[, …args]): 用于发布(触发)指定的事件。可以传递事件名称和一些可选的参数,以便在事件被触发时传递给事件处理函数。 订阅者:on(eventName, listener): 用于订阅(监听)指定的事件。当事件被触发时,已注册的监听器listener函数会被调用。可以为同一个事件名称注册多个监听器。

手写发布订阅

// 实现⾃定义事件
// 编写⼀个简单的⾃定义事件处理器
// 1. 具备 on ⽅法绑定事件
// 2. 具备 off ⽅法解绑事件
class EventEmitter {
    constructor() {
        this.events = {}; // 调度中心
    }
    // 监听器
    on(event, listener) {
        if(this.events[event]) {
            this.events[event].push(listener);
        } else {
            this.events[event] = [listener];
        }
    }
    // 触发器
    emit(event, ...args) {
        // 先处理通配符
        if(event === '*') {
            Object.keys(this.events).forEach(key => {
                this.events[key].forEach(listener => {
                    listener(...args);
                });
            });
            return;
        }
        if(this.events[event]) {
            this.events[event].forEach(listener => {
                listener(...args);
            });
        }
    }
    off(event, listener) {
        if(this.events[event] && listener) {
            this.events[event] = this.events[event].filter(l => l !== listener);
        } else {
            delete this.events[event];
        }
    }
}


var emitter = new EventEmitter();

emitter.on('foo', function(e){
    console.log('listening foo event 1', e);
});

emitter.on('foo', function(e){
    console.log('listening foo event 2', e);
});

emitter.on('bar', function(e){
    console.log('listening bar event', e);
});

// // 监听全部事件
emitter.on('*', function(e){
    // 意思就是不管什么事件被触发,都会被监听到
    console.log('listening all events');
});

emitter.emit('foo', {name : 'John'});
emitter.emit('bar', {name : 'Sun'});
emitter.emit('*', {name : 'Sun'}); // 先触发,后监听到,再执行监听回调

emitter.off('foo');