前端开发中常用的几种设计模式(三)

简介: 前端开发中常用的几种设计模式

行为型模式

1. 策略模式

策略模式是一种简单却常用的设计模式,它的应用场景非常广泛。我们先了解下策略模式的概念,再通过代码示例来更清晰的认识它。

策略模式由两部分构成:一部分是封装不同策略的策略组,另一部分是 Context。通过组合和委托来让 Context 拥有执行策略的能力,从而实现可复用、可扩展和可维护,并且避免大量复制粘贴的工作。

20210817215442546.jpg

策略模式的典型应用场景是表单校验中,对于校验规则的封装。接下来我们就通过一个简单的例子具体了解一下:

/**
 * 登录控制器
 */
function LoginController() {
  this.strategy = undefined;
  this.setStrategy = function (strategy) {
    this.strategy = strategy;
    this.login = this.strategy.login;
  }
}
/**
 * 用户名、密码登录策略
 */
function LocalStragegy() {
  this.login = ({ username, password }) => {
    console.log(username, password);
    // authenticating with username and password... 
  }
}
/**
 * 手机号、验证码登录策略
 */
function PhoneStragety() {
  this.login = ({ phone, verifyCode }) => {
    console.log(phone, verifyCode);
    // authenticating with hone and verifyCode... 
  }
}
/**
 * 第三方社交登录策略
 */
function SocialStragety() {
  this.login = ({ id, secret }) => {
    console.log(id, secret);
    // authenticating with id and secret... 
  }
}
const loginController = new LoginController();
// 调用用户名、密码登录接口,使用LocalStrategy
app.use('/login/local', function (req, res) {
  loginController.setStrategy(new LocalStragegy());
  loginController.login(req.body);
});
// 调用手机、验证码登录接口,使用PhoneStrategy
app.use('/login/phone', function (req, res) {
  loginController.setStrategy(new PhoneStragety());
  loginController.login(req.body);
});
// 调用社交登录接口,使用SocialStrategy
app.use('/login/social', function (req, res) {
  loginController.setStrategy(new SocialStragety());
  loginController.login(req.body);
});

从以上示例可以得出使用策略模式有以下优势:

  1. 方便在运行时切换算法和策略
  2. 代码更简洁,避免使用大量的条件判断
  3. 关注分离,每个strategy类控制自己的算法逻辑,strategy和其使用者之间也相互独立

2. 观察者模式

观察者模式又叫发布订阅模式(Publish/Subscribe),它定义了一种一或一对多的关系,让多个观察者对象同时监听某一个主题对象,这个主题对象的状态发生变化时就会通知所有的观察者对象,使得它们能够自动更新自己。典型代表vue/react等。


使用观察者模式的好处:

  1. 支持简单的广播通信,自动通知所有已经订阅过的对象。
  2. 目标对象与观察者存在的是动态关联,增加了灵活性。
  3. 目标对象与观察者之间的抽象耦合关系能够单独扩展以及重用。


20210817163309141.png

当然给元素绑定事件的addEventListener()也是一种:

target.addEventListener(type, listener [, options]);

Target就是被观察对象Subject,listener就是观察者Observer。


观察者模式中Subject对象一般需要实现以下API:


subscribe(): 接收一个观察者observer对象,使其订阅自己

unsubscribe(): 接收一个观察者observer对象,使其取消订阅自己

fire(): 触发事件,通知到所有观察者

用JavaScript手动实现观察者模式:

// 被观察者
function Subject() {
  this.observers = [];
}
Subject.prototype = {
  // 订阅
  subscribe: function (observer) {
    this.observers.push(observer);
  },
  // 取消订阅
  unsubscribe: function (observerToRemove) {
    this.observers = this.observers.filter(observer => {
      return observer !== observerToRemove;
    })
  },
  // 事件触发
  fire: function () {
    this.observers.forEach(observer => {
      observer.call();
    });
  }
}

验证一下订阅是否成功:

const subject = new Subject();
function observer1() {
  console.log('Observer 1 Firing!');
}
function observer2() {
  console.log('Observer 2 Firing!');
}
subject.subscribe(observer1);
subject.subscribe(observer2);
subject.fire();

验证一下取消订阅是否成功:

1. subject.unsubscribe(observer2);
2. subject.fire()

输出:

Observer 1 Firing!

3. 迭代器模式

ES6中的迭代器 Iterator 相信大家都不陌生,迭代器用于遍历容器(集合)并访问容器中的元素,而且无论容器的数据结构是什么(Array、Set、Map等),迭代器的接口都应该是一样的,都需要遵循 迭代器协议


迭代器模式解决了以下问题:

  1. 提供一致的遍历各种数据结构的方式,而不用了解数据的内部结构
  2. 提供遍历容器(集合)的能力而无需改变容器的接口


20210817220744492.png

一个迭代器通常需要实现以下接口:

  • hasNext():判断迭代是否结束,返回Boolean
  • next():查找并返回下一个元素

为Javascript的数组实现一个迭代器可以这么写:


const item = [1, 'red', false, 3.14];
function Iterator(items) {
  this.items = items;
  this.index = 0;
}
Iterator.prototype = {
  hasNext: function () {
    return this.index < this.items.length;
  },
  next: function () {
    return this.items[this.index++];
  }
}

验证一下迭代器:

1. const iterator = new Iterator(item);
2. 
3. while(iterator.hasNext()){
4. console.log(iterator.next());
5. }

输出:

1, red, false, 3.14

ES6提供了更简单的迭代循环语法 for...of,使用该语法的前提是操作对象需要实现 可迭代协议(The iterable protocol),简单说就是该对象有个Key为 Symbol.iterator 的方法,该方法返回一个iterator对象。


比如我们实现一个 Range 类用于在某个数字区间进行迭代:

function Range(start, end) {
  return {
    [Symbol.iterator]: function () {
      return {
        next() {
          if (start < end) {
            return { value: start++, done: false };
          }
          return { done: true, value: end };
        }
      }
    }
  }
}

验证:

1. for (num of Range(1, 5)) {
2. console.log(num);
3. }


结果:

1, 2, 3, 4

4. 状态模式

状态模式:一个对象有状态变化,每次状态变化都会触发一个逻辑,不能总是用if...else来控制。

20210817221352327.png

比如红绿灯:

// 状态(红灯,绿灯 黄灯)
class State {
    constructor(color) {
        this.color = color;
    }
    // 设置状态
    handle(context) {
        console.log(`turn to ${this.color} light`);
        context.setState(this)
    }
}
// 主体
class Context {
    constructor() {
        this.state = null;
    }
    // 获取状态
    getState() {
        return this.state;
    }
    setState(state) {
        this.state = state;
    }
}
// 测试
let context = new Context();
let green = new State('green');
let yellow = new State('yellow');
let red = new State('red');
// 绿灯亮了
green.handle(context);
console.log(context.getState())
// 黄灯亮了
yellow.handle(context);
console.log(context.getState())
// 红灯亮了
red.handle(context);
console.log(context.getState())

设计原则验证

将状态对象和主体对象分离,状态的变化逻辑单独处理

符合开放封闭原则

相关文章
|
3天前
|
设计模式 存储 缓存
精进前端开发:深入探讨前端设计模式
精进前端开发:深入探讨前端设计模式
33 0
|
3天前
|
设计模式 前端开发 开发者
探索现代前端开发中的设计模式
在现代前端开发中,设计模式是一种重要的工具,它可以帮助开发者提高代码的可维护性、可扩展性和可重用性。本文将介绍几种常见的设计模式,并探讨它们在前端开发中的应用。
|
3天前
|
设计模式 前端开发 算法
前端工程中的设计模式应用(下)
前端工程中的设计模式应用(下)
|
1天前
|
设计模式 存储 前端开发
JS的几种设计模式,Web前端基础三剑客学习知识分享,前端零基础开发
JS的几种设计模式,Web前端基础三剑客学习知识分享,前端零基础开发
|
3天前
|
设计模式 前端开发 JavaScript
从设计模式看前端开发中的数据管理
【2月更文挑战第2天】在前端开发中,合理的数据管理是至关重要的。本文将从设计模式的角度出发,探讨在前端开发中如何应用设计模式来优化数据管理,提高代码质量和可维护性。
16 0
|
10月前
|
设计模式 前端开发 数据安全/隐私保护
前端实现设计模式之状态模式
状态模式是一种行为型设计模式,用于在对象内部状态发生变化时改变其行为。状态模式将对象的行为封装在不同的状态类中,使得对象在不同的状态下可以有不同的行为。在前端开发中,状态模式常用于管理组件的状态和行为,以实现复杂的交互逻辑。本文将介绍状态模式的概念和应用,并提供具体的代码示例和解读。
113 0
|
3天前
|
设计模式 前端开发 算法
前端工程中的设计模式应用(上)
前端工程中的设计模式应用(上)
|
3天前
|
设计模式 存储 缓存
前端工程中的设计模式应用(中)
前端工程中的设计模式应用(中)
|
5月前
|
设计模式 存储 缓存
精进前端开发:深入探讨前端设计模式
精进前端开发:深入探讨前端设计模式
35 1
|
7月前
|
设计模式 JSON 前端开发
前端面试必看(手写Promise+js设计模式+继承+函数柯里化等)JavaScript面试全通关(1/3)
前端面试必看(手写Promise+js设计模式+继承+函数柯里化等)JavaScript面试全通关(1/3)
43 0