Shu-How Zの小窝

Loading...

设计模式简介

设计模式简介

最佳的实践得来的设计经验 设计模式 开发过程中面临的一般问题的解决方案

相当长的一段时间的试验和错误总结出来

设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结

设计模式原则

  1. S – Single Responsibility Principle 单一职责原则 一个程序只做好一件事 如果功能过于复杂就拆分开,每个部分保持独立
  2. O – OpenClosed Principle 开放/封闭原则 对扩展开放,对修改封闭 增加需求时,扩展新代码,而非修改已有代码
  3. L – Liskov Substitution Principle 里氏替换原则 子类能覆盖父类 父类能出现的地方子类就能出现
  4. I – Interface Segregation Principle 接口隔离原则 保持接口的单一独立 类似单一职责原则,这里更关注接口
  5. D – Dependency Inversion Principle 依赖倒转原则 面向接口编程,依赖于抽象而不依赖于具体 使用方只关注接口而不关注具体类的实现

创建形

单例模式

一个类只有一个实例,并提供一个访问它的全局访问点

class Axios{
    constructor(config){
        this.config = config;
    }
}
Axios.getInstance = function(config){
    if(!this.instance){
        this.instance = new Axios(config);
    }
    return this.instance;
}
let i1=Axios.getInstance({});
let i2=Axios.getInstance({});
console.log(i1==i2);

优点
1.命名空间,减少全局变量
2.增强模块性,集中变量/逻辑管理
3.且只会实例化一次。简化了代码的调试和维护

缺点
1.单点访问,导致模块间的强耦合 从而不利于单元测试。

场景例子
1.命名空间和实现分支型方法
2.登录框
3.vuex 和 redux中的store

class Axios{
    constructor(config){
        this.config = config;
    }
    static getInstance(config){
        if(!this.instance){
            this.instance = new Axios(config);
        }
        return this.instance;
    }
}

原型模式

原型模式(prototype)是指用原型实例指向创建对象的种类,并且通过拷贝这些原型创建新的对象。

原型模式,就是创建一个共享的原型,通过拷贝这个原型来创建新的类,用于创建重复的对象,带来性能上的提升

class Person {
  constructor(name) {
    this.name = name;
  }
}
class Student extends Person {
  constructor(name, grade) {
    super(name);
  }
  sayName(){
    console.log(this.name,111111111111111111);
  }
}
var s = new Student("John", 10);
s.sayName();

//继承

工厂模式

  • 在不暴露创建对象的具体逻辑,而是将逻辑进行封装,那么它就可以被称为工厂。
优点 
1.调用者创建对象时只要知道其名称即可 扩展性高,如果要新增一个产品,直接扩展一个工厂类即可。 
2.隐藏产品的具体实现,只关心产品的接口。 
缺点 
1.每次增加一个产品时,都需要增加一个具体类,这无形增加了系统内存的压力和系统的复杂度,也增加了具体类的依赖

黑盒----------
JQuery的$()就是一个工厂函数
vue 的异步组件

class Factory {
  create(name) {
    return new Product(name);
  }
}
class Product {
  constructor(name) {
    this.name = name;
  }
  printName() {
    console.log(this.name);
  }
}
var factory = new Factory();
var product = factory.create("test");
product.printName();

-------------------------------------------------------------------------
// 抽象工厂模式    
class ProductionFlow {
  constructor() {
    if (new.target === ProductionFlow) {
      throw new Error("抽象类不能被实例");
    }
  }
  production() {
    throw new Error("production要被重写");
  }
  materials() {
    throw new Error("materials要被重写");
  }
}
class TShirt extends ProductionFlow {
  production() {
    console.log(`材料:${this.materials()},生产t恤`);
  }
  materials() {
    return "纯棉";
  }
}

function getAbstractProductionFactory(clothingType) {
  const clothingObj = {
    t_shirt: TShirt, //add more...
  };
  if (clothingObj[clothingType]) {
    return clothingObj[clothingType];
  }
  throw new Error(`工厂暂时不支持生产这个${clothingType}类型的服装`);
}
const tShirtClass = getAbstractProductionFactory("t_shirt");

const tShirt = new downTShirtClass();
tShirt.production();

建造者模式

建造者模式是一种比较复杂使用频率较低的创建型设计模式,建造者模式为客户端返回的不是一个简单的产品,而是一个由多个部件组成的复杂产品。主要用于将一个复杂对象的构建与他的表现分离,使得同样的构建过程可以创建不同的表示。

  1. 构建与他的表现分离
优点
1.建造者独立易扩展
2.方便控制细节风险
缺点
1.产品必须有共同点,范围有限制
2.当内部有变化复杂时,会有很多建造类

构建与他的表现分离

class Product {
  constructor(name, price) {
    this.name = name;
    this.price = price;
  }
}
class Pc extends Product {
     constructor(name, price) {
        super(name, price);
    }
}
class Mobile extends Product {
    constructor(name, price) {
       super(name, price);
   }
}
class ProductManage{
    constructor() {
        this.products = [];
    }
    addProduct(product) {
        this.products.push(product);
    }
    getSimplePrice(){
        return this.products.reduce((total, product) => total + product.price, 0);
    }
}
class FactorManger {
    createPc(){
        throw new Error("Not implemented");
    }
    createMobile(){
        throw new Error("Not implemented");
    }
}

class Worker extends FactorManger {
    constructor() {
        super();
        this.productarr = new ProductManage();
    }
    createPc(createNumProduct){
        for(let i=0;i<createNumProduct;i++){
            this.productarr.addProduct(new Pc("Pc", 1000));
        }
    }
    createMobile(createNumProduct){
        for(let i=0;i<createNumProduct;i++){
            this.productarr.addProduct(new Mobile("Mobile", 1));
        }

    }
}

class Salesman {
    constructor() {
        this.Worker=null;
    }
    setWorker(worker){
        this.Worker=worker;
    }
    orderTotalPrice(products){
        products.forEach(item => {
            if(item.name === "Pc"){
                this.Worker.createPc(item.num);
            }else if(item.name === "Mobile"){
                this.Worker.createMobile(item.num);
            }
            else{
                throw new Error("Not implemented");
            }
        });
        return this.Worker.productarr.getSimplePrice();
    }
}

const salesman = new Salesman()
const worker = new Worker()
salesman.setWorker(worker)
const order = [
  {
    name: 'Pc',
    num: 10
  },
  {
    name: 'Mobile',
    num: 4
  },
]
console.log(`本次订单所需金额:${salesman.orderTotalPrice(order)}`) //10004

结构型

适配器模式

`将一个类的接口转化为另外一个接口.解决使类之间接口不兼容问题

优点
1.可以让任何两个没有关联的类一起运行。
2.提高了类的复用性、透明度、以及灵活性
3.适配对象,适配库,适配数据

缺点
额外对象的创建,非直接调用,存在一定的开销
过多的使用适配器模式,会让系统变得零乱,不易整体把控。
非必要用适配器模式,就重构最好,若用的话,文档完善

场景
整合第三方SDK diy $.axjax
封装旧接口  vue.computed


适配器与代理模式相似
适配器模式: 提供一个不同的接口
代理模式: 提供一模一样的接口

class Person{
    getName(){
        return "John";
    }
}
class Adapter{
    constructor(person){
        this.person = person;
    }
    getFullName(){
        return this.person.getName();
    }
}
let person = new Person();
let adapter = new Adapter(person);
console.log(adapter.getFullName());

装饰器模式

装饰者模式在不更改源代码情况下,对其进行职责添加。相比于继承装饰器的更轻巧。
通俗的讲我们给心爱的手机上贴膜,带手机壳,贴纸,这些就是对手机的装饰。
优点
	装饰类和被装饰类它们之间可以相互独立发展,不会相互耦合,
    装饰器模式是继承的一个替代模式,它可以动态的扩展一个实现类的功能。
缺点
	多层的装饰会增加复杂度
class Phone{
    create(){
        console.log("Phone created");
    }
}

class Decorator{
    constructor(phone){
        this.phone = phone;
    }
    create(){
        this.phone.create();
        this.addPhoneBox();
        this.addPhonePicker();
    }
    addPhoneBox(){
        console.log("Phone box added");
    }
    addPhonePicker(){
        console.log("Phone picker added");
    }
}
let phone = new Phone();
let decorator = new Decorator(phone);
decorator.create();   

代理模式

优点
	代理模式能将代理对象与被调用对象分离,降低了系统的耦合度。
	之间起到一个中介作用,这样可以起到保护目标对象的作用
	代理对象可以扩展目标对象的功能
	通过修改代理对象就可以了,符合开闭原则;
缺点
	处理请求速度可能有差别,非直接访问存在开销
    
// 员工类
class Staff {
  constructor(task) {
    this.task = task;
  }
  applyFor(target){
    target.receiveAplyFor(this.task)
  }
}
// 秘书类 中间人 代理
class Secretary {
  constructor() {
    this.leader=new Leader();
  }
  receiveAplyFor(task) {
    this.leader.receiveAplyFor(task);
  }
}
// 领导类
class Leader{
    receiveAplyFor(task) {
        console.log('批准: ' + task);
    }
}
const staff = new Staff('升职加薪')
staff.applyFor(new Secretary()) // 批准:升职加薪    

外观模式

外观模式本质就是封装交互,隐藏系统的复杂性,提供一个可以访问的接口。
由一个将子系统一组的接口集成在一起的高层接口,以提供一个一致的外观,减少外界与多个子系统之间的直接交互,从而更方便的使用子系统。
优点
	减少系统相互依赖。
	提高灵活性。
	提高了安全性
缺点
	不符合开闭原则,如果要改东西很麻烦,继承重写都不合适。
    
// 外观模式
function addEvent(el,type,fn){
    if(el.addEventlistener){// 高级游览器添加事件DOM API
        el.addEventlistener(type,fn,false)
    }else if(el.attachEvent){// 低版本游览器的添加事件API
        el.attachEvent(`on${type}`,fn)
    }else {//其他
        el[type] = fn
    }
}

二层金字塔
	1
2	2	2

桥接模式

桥接模式(Bridge)将抽象部分与它的实现部分分离,使它们都可以独立地变化。
优点
	有助于独立地管理各组成部分, 把抽象化与实现化解耦
	提高可扩充性
缺点
	大量的类将导致开发成本的增加,同时在性能方面可能也会有所减少。
    桥接模式的引入会增加系统的理解与设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程。
//桥接模式(Bridge)
class color{
    constructor(name){
        this.name = name;
    }
}
class shape{
    constructor(name,color){
        this.name = name;
        this.color = color;
    }
    show(){
        console.log(this.name + this.color.name);
    }
}
let red = new color('红色');
let yellow = new color('黄色');
let circle = new shape('圆形',red);
let triangle = new shape('三角形',yellow);
circle.show();
triangle.show();

组合模式

组合模式
组合模式就是由一些小的子对象构建出的更大的对象,而这些小的子对象本身可能也是由多个孙对象组合而成的。
组合模式将对象组合成树状结构,以表示“部分-整体”的层次结构。除了用来表示树状结构之外,组合模式的另一个好处就是通过对象的多态性表现,使得用户对单个对象和组合对象的使用具有一致性。

缺点
如果通过组合模式创建了太多的对象,那么这些对象可能会让系统负担不起。

优点
高层模块调用简单,节点可以自由添加

// 组合模式
class Train{
    create(){
        console.log('创建火车票订单')
    }
}
class Hotal{
    create(){
        console.log('创建酒店订单')
    }
}
class TotalOrder{
    constructor(){
        this.orderList = [];
    }
    add(order){
        this.orderList.push(order);
        return this;
    }
    create(){
        this.orderList.forEach(order=>{
            order.create();
        })
        return this;
    }
}
let totalOrder = new TotalOrder();
// 可以在购票网站买车票同时也订房间
totalOrder.add(new Train()).add(new Hotal()).create();

享元模式

运用共享技术有效地支持大量细粒度对象的复用。
享元模式的关键是如何区分内部状态和外部状态
	内部状态:可以被对象共享,通常不会改变的称为内部状态
	外部状态:取决于具体的场景,根据具体的场景变化,并且不能被共享的称为外部状态
优点
	大大减少对象的创建,降低系统的内存,使效率提高。
缺点
	提高了系统的复杂度,需要分离出外部状态和内部状态,而且外部状态具有固有化的性质, 不应该随着内部状态的变化而变化,否则会造成系统的混乱
    
let id = 0
// 定义内部状态
class Upload {
  constructor(uploadType) {
    this.uploadType = uploadType
  }
  // 点击删除时 小于3000直接删除,大于3000通过confirm提示弹窗删除。
  delFile(id) {
    uploadManager.setExternalState(id,this)
    if(this.fileSize < 3000){
      return this.dom.parentNode.removeChild(this.dom)
    }
    if(window.confirm(`确定要删除该文件吗?${this.fileName}`)){
      return this.dom.parentNode.removeChild(this.dom)
    }
  }
}
// 外部状态
class uploadManager {
  static uploadDatabase = {}
  static add(id, uploadType, fileName, fileSize) {
    const filWeightObj = UploadFactory.create(uploadType)
    const dom = this.createDom(fileName, fileSize, () => {
      filWeightObj.delFile(id)
    })
    this.uploadDatabase[id] = {
      fileName,
      fileSize,
      dom
    }
  }
  // 创建DOM 并且为button绑定删除事件。
  static createDom(fileName, fileSize, fn) {
    const dom = document.createElement('div')
    dom.innerHTML = `
      <span>文件名称:${fileName},文件大小:${fileSize}</span>
      <button class="delFile">删除</button>
    `
    dom.querySelector('.delFile').onclick = fn
    document.body.append(dom)
    return dom
  }
  static setExternalState(id, flyWeightObj) {
    const uploadData = this.uploadDatabase[id]
    for (const key in uploadData) {
      if (Object.hasOwnProperty.call(uploadData, key)) {
        flyWeightObj[key] = uploadData[key]
      }
    }
  }

}
// 定义一个工厂创建upload对象,如果其内部状态实例对象存在直接返回,反之创建保存并返回。
class UploadFactory {
  static createFlyWeightObjs = {}
  static create(uploadType) {
    if (this.createFlyWeightObjs[uploadType]) {
      return this.createFlyWeightObjs[uploadType]
    }
    return this.createFlyWeightObjs[uploadType] = new Upload(uploadType)
  }
}
// 开始加载
const startUpload = (uploadType, files)=>{
    for (let i = 0, file; file = files[i++];) {
      uploadManager.add(++id, uploadType, file.fileName, file.fileSize)
    }
}

startUpload('plugin', [
  {fileName: '1.txt',fileSize: 1000},
  {fileName: '2.html',fileSize: 3000},
  {fileName: '3.txt',fileSize: 5000}
]);
startUpload('flash', [
  {fileName: '4.txt',fileSize: 1000},
  {fileName: '5.html',fileSize: 3000},
  {fileName: '6.txt',fileSize: 5000}
]);
    

行为型

观察者模式/发布订阅

发布订阅又称观察者模式,它定义对象之间的1对N的依赖关系,当其中一个对象发生变化时,所有依赖于它的对象都会得到通知。

优点
支持简单的广播通信,自动通知所有已经订阅过的对象
目标对象与观察者之间的抽象耦合关系能单独扩展以及重用
增加了灵活性
观察者模式所做的工作就是在解耦,让耦合的双方都依赖于抽象,而不是依赖于具体。从而使得各自的变化都不会影响到另一边的变化。

缺点
过度使用会导致对象与对象之间的联系弱化,会导致程序难以跟踪维护和理解

//  观察者模式/发布订阅
class Subject {
  constructor() {
    this.state = 0;
    this.observers = [];
  }
  getState(){
    return this.state;
  }
  setState(state) {
    this.state = state;
    this.notifyAllObservers();
  }
  notifyAllObservers() {
    this.observers.forEach(observer => observer.update());
  }
  attach(observer) {
    this.observers.push(observer);
  }
}

// 观察者
class Observer {
  constructor(name, subject) {
    this.name = name;
    this.subject = subject;
    this.subject.attach(this);
  }
  update() {
    console.log(`${this.name} update, state: ${this.subject.getState()}`);
  }
}

const subject = new Subject();
const observer1 = new Observer('observer1', subject);
const observer2 = new Observer('observer2', subject);
subject.setState(99);

迭代器模式

迭代器模式是指提供一种方法顺序访问一个聚合对象中的每个元素,并且不需要暴露该对象的内部。
优点
它支持以不同的方式遍历一个聚合对象。
迭代器简化了聚合类。在同一个聚合上可以有多个遍历。
在迭代器模式中,增加新的聚合类和迭代器类都很方便,无须修改原有代码。
缺点
由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性。

class Iterator{
    constructor(container){
        this.list = container.list;
        this.index = 0;
    }
    next(){
        if(this.hasNext){
            return this.list[this.index++];
        }
        return null
    }
    hasNext(){
        if(this.index>=this.list.length){
            return false;
        }
        return true;
    }
}

class Container{
    constructor(list){
        this.list = list;
    }
    getIterator(){
        return new Iterator(this);
    }
}

let container = new Container([1, 2, 3, 4, 5])
let iterator = container.getIterator()
while(iterator.hasNext()) {
  console.log(iterator.next())
}

策略模式

策略模式指的是定义一系列算法,把他们一个个封装起来,目的就是将算法的使用和算法的实现分离开来。同时它还可以用来封装一系列的规则,比如常见的表单验证规则,只要这些规则指向的目标一致,并且可以被替换使用,那么就可以用策略模式来封装它们。
优点
	算法可以自由切换,避免了使用多层条件判断,增加了扩展性
缺点
	策略类增多,所有策略类都需要对外暴露。
    
//策略模式
const rules = {
    cover_img: {
        must: false,
        msg: '请上传封面图片',
        val: ''
    },
    name: {
        must: true,
        msg: '姓名不能为空',
        val: ''
    },
    sex: {
        must: true,
        msg: '请填写性别',
        val: ''
    },
    birthday: {
        must: false,
        msg: '请选择生日',
        val: ''
    },
}
function verify(){
    for(const key in rules){
        if(rules[key].must&&!rules[key].val){
            console.log(rules[key].msg)
        }
    }
}
verify()
// 姓名不能为空
// 请填写性别
    

模板方法模式

模板方法模式由两部分结构组成,第一部分是抽象父类,第二部分是具体的实现子类。通常在抽象父类中封装了子类的算法框架,包括实现一些公共方法和封装子类中所有方法的执行顺序。子类通过继承这个抽象类,也继承了整个算法结构,并且可以选择重写父类的方法。
优点
	提取了公共代码部分,易于维护
缺点
	增加了系统复杂度,主要是增加了的抽象类和类间联系
    
// 模板方法模式

class Beverage {
    constructor({brewDrink, addCondiment}) {
        this.brewDrink = brewDrink
        this.addCondiment = addCondiment
    }
    /* 烧开水,共用方法 */
    boilWater() { console.log('水已经煮沸=== 共用') }
    /* 倒杯子里,共用方法 */
    pourCup() { console.log('倒进杯子里===共用') }
    /* 模板方法 */
    init() {
        this.boilWater()
        this.brewDrink()
        this.pourCup()
        this.addCondiment()
    }
}
/* 咖啡 */
const coffee = new Beverage({
     /* 冲泡咖啡,覆盖抽象方法 */
     brewDrink: function() { console.log('冲泡咖啡') },
     /* 加调味品,覆盖抽象方法 */
     addCondiment: function() { console.log('加点奶和糖') }
})
coffee.init() 

职责链模式

场景例子
	JS 中的事件冒泡
	作用域链
	原型链
优点
	降低耦合度,它将请求的发送者和接收者解耦。
	简化了对象,使得对象不需要知道链的结构。
	增强给对象指派职责的灵活性。通过改变链内的成员或者调动它们的次序,允许动态地新增或者删除责任。
缺点
	不能保证每一条请求都一定被接收。
	系统性能将受到一定影响,而且在进行代码调试时不太方便,可能会造成循环调用。
	可能不容易观察运行时的特征,有碍于排除问题。
    
// 职责链模式
// 请假审批,需要组长审批、经理审批、总监审批
class Action{
    constructor(name){
        this.name = name;
        this.nextAction = null;
    }
    setNextAction(action){
        this.nextAction = action;
    }
    handle(){
        console.log(`${this.name} 审批通过`);
        if(this.nextAction != null){
            this.nextAction.handle();
        }
    }
}
let a1 = new Action("组长")
let a2 = new Action("经理")
let a3 = new Action("总监")
a1.setNextAction(a2)
a2.setNextAction(a3)
a1.handle()    

命令模式

优点
	对命令进行封装,使命令易于扩展和修改
	命令发出者和接受者解耦,使发出者不需要知道命令的具体执行过程即可执行
缺点
	使用命令模式可能会导致某些系统有过多的具体命令类
    
//命令模式

// 接收者类
class Receiver {
  execute() {
    console.log("接收者执行请求");
  }
}

// 命令者
class Command {
  constructor(receiver) {
    this.receiver = receiver;
  }
  execute() {
    console.log("命令");
    this.receiver.execute();
  }
}

// 触发者
class Invoker {
  constructor(command) {
    this.command = command;
  }
  invoke() {
    console.log("开始");
    this.command.execute();
  }
}

// 仓库
const warehouse = new Receiver();   
// 订单    
const order = new Command(warehouse);  
// 客户
const client = new Invoker(order);      
client.invoke()
    

备忘录模式

备忘录模式就是在不破坏封装的前提下,捕获一个对象内部状态,并在该对象之外保存这个状态,以保证以后可以将对象恢复到原先的状态。
优点
	给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便地回到某个历史的状态
缺点
	消耗资源。如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存。
// 备忘录模式

// 备忘类
class Memento{
    constructor(content){
        this.content = content;
    }
    getContent(){
        return this.content;
    }
}

// 备忘列表
class CareTaker{
    constructor(){
        this.list=[];
    }
    add(memento){
        this.list.push(memento);
    }
    get(index){
        return this.list[index];
    }
}

// 编辑器
class Editor{
    constructor(){
        this.content = null;
    }
    setContent(content){
        this.content = content;
    }
    getContent(){
        return this.content;
    }
    saveContentToMemento(){
        return new Memento(this.content);
    }
    getContentFromMemento(memento){
        this.content = memento.getContent();
    }
}

//测试代码
let editor = new Editor()
let careTaker = new CareTaker()

editor.setContent('111')
editor.setContent('222')
careTaker.add(editor.saveContentToMemento())
editor.setContent('333')
careTaker.add(editor.saveContentToMemento())
editor.setContent('444')

console.log(editor.getContent()) //444
editor.getContentFromMemento(careTaker.get(1))
console.log(editor.getContent()) //333

editor.getContentFromMemento(careTaker.get(0))
console.log(editor.getContent()) //222

状态模式

场景
	一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为
	一个操作中含有大量的分支语句,而且这些分支语句依赖于该对象的状态

优点
	定义了状态与行为之间的关系,封装在一个类里,更直观清晰,增改方便
	状态与状态间,行为与行为间彼此独立互不干扰
	用对象代替字符串来记录当前状态,使得状态的切换更加一目了然
    
缺点
	会在系统中定义许多状态类
	逻辑分散
    
// 状态模式

class State{
    constructor(attack){
        this.attack = attack;
    }
    handle(context){
        console.log(this.attack);
        context.setState(this);
    }
}

class Context{
    constructor(){
        this.state=null;
    }
    getState(){
        return this.state;
    }
    setState(state){
        this.state = state;
    }
}

const q1 = new State('q1 第1击'),
      q2 = new State('q2 第2击'),
      q3 = new State('q3 第3击'),
      context = new Context();

q1.handle(context)//q1 第1击
q2.handle(context)//q2 第2击
q3.handle(context)//q3 第3击

访问者模式

访问者模式是将数据的操作和数据的结构进行分离,对数据中各元素的操作封装独立的类,使其在不改变数据结构情况下扩展新的数据。
优点
	符合单一职责原则。具有优秀的扩展性和灵活性。
缺点
	违反了依赖倒置原则,依赖了具体类,没有依赖抽象。

// 访问者模式

// 访问者
class Visitor {
    constructor(){}
    visitConcreteElement(ConcreteElement){
        ConcreteElement.operation()
    }
}

// 元素类  
class ConcreteElement {
    constructor(){}
    operation(){
        console.log('ConcreteElement.operation invoked')
    }
    accept(visitor){
        visitor.visitConcreteElement(this)
    }
}

// 客户端
const visitor = new Visitor()
const element = new ConcreteElement()
element.accept(visitor)

中介者模式

中介者模式的作用就是解除对象与对象之间的紧密耦合关系。增加一个中介者对象之后,所有相关对象都通过中介者对象来通信,而不是相互引用,所以当一个对象发生改变时,只需要通过中介者对象即可。中介者使各对象之间耦合松散,而且可以独立改变他们之间的交互。中介者模式使网状的多对多关系变成了相对简单的一对多关系。
优点
	降低了类的复杂度,将一对多转化成了一对一。各个类之间的解耦。
缺点
	当中介者变得庞大复杂,导致难以维护。
    
// 中介者模式

class A {
    constructor() {
        this.number = 0
    }
    setNumber(num, m) {
        this.number = num
        if (m) {
            m.setB()
        }
    }
}
class B {
    constructor() {
        this.number = 0
    }
    setNumber(num, m) {
        this.number = num
        if (m) {
            m.setA()
        }
    }
}

class Mediator {
    constructor(a, b) {
        this.a = a
        this.b = b
    }
    setA() {
        let number = this.b.number
        this.a.setNumber(number * 10)
    }
    setB() {
        let number = this.a.number
        this.b.setNumber(number / 10)
    }
}

let a = new A()
let b = new B()
let m = new Mediator(a, b)
a.setNumber(10, m)
console.log(a.number, b.number)
b.setNumber(10, m)
console.log(a.number, b.number)

解释器模式

解释器模式提供了评估语言的语法或表达式的方式,它属于行为型模式。这种模式实现了一个表达式接口该接口,该接口解释一个特定的上下文。
优点
	可扩展性比较好,灵活。增加了新的解释表达式的方式。
缺点
	可利用场景比较少,在web开发中几乎不可见。对于复杂的环境比较难维护。
	解释器模式会引起类膨胀。它还采用递归调用方法,没控制好可能会导致崩溃。

// 解释器模式

class Context{
    constructor(){
        this._list=[];
        this._sum=0;
    }
    get sum(){
        return this._sum;
    }
    set sum(newValue){
        this._sum=newValue;
    }
    add(expression){
        this._list.push(expression);
    }
    get list(){
        return [...this._list];
    }
}

class PlusExpression{
    interpret(context){
        if(!(context instanceof Context)){
            throw new Error("TypeError");
        }
        context.sum=++context.sum;
    }
}

class MinusExpression{
    interpret(context){
        if(!(context instanceof Context)){
            throw new Error("TypeError");
        }
        context.sum=--context.sum;
    }
}

  /** 以下是测试代码 **/
  const context = new Context();
  
  // 依次添加: 加法 | 加法 | 减法 表达式
  context.add(new PlusExpression());
  context.add(new PlusExpression());
  context.add(new MinusExpression());
  
  // 依次执行: 加法 | 加法 | 减法 表达式
  context.list.forEach(expression => expression.interpret(context));
  console.log(context.sum);
posted @ 2026-04-27 17:07  KooTeam  阅读(6)  评论(0)    收藏  举报