前端必须知道的设计模式

前言:我想作为前端开发从业人员,都大致听说过什么是设计模式,之前看到一本书的名字就叫JavaScript设计模式。

ps:书籍资料可以自行搜索pdf文档查看

什么是设计模式

设计模式是软件设计中常见问题的解决方案,这些模式易于重复使用且富有表现力。

维基百科介绍: 在软件工程中,软件设计模式是针对软件设计中给定上下文中常见问题的通用可重复使用解决方案。它不是可以直接转换为源代码或机器代码的完成设计。它是一种如何解决问题的描述或模板,可用于许多不同情况。

设计模式的类型

  1. 创建型
  2. 结构型
  3. 行为

1.创造-建型设计模式

顾名思义创建型模式将创建对象,而不是直接实例化一个对象

维基百科介绍:在软件工程中,创建型设计模式是处理对象创建机制的设计模式,尝试以适合情况的方式创建对象。对象创建的基本形式可能会导致设计问题或增加设计的复杂性。创建型设计模式通过某种方式控制此对象创建来解决此问题。

1.1 工厂方法:它用于创建单个对象接口,并让子类决定实例化哪个类。

示例如下:有一个类点,创建一个笛卡尔点和一个极点,将定义一个点工厂类实现

js 复制代码
CoordinateSystem = {
    CARTESIAN: 0,
    POLAR: 1,
}

// 一个点类
class Point {
    constructor(x, y) {
        this.x = x
        this.y = y
    }
    
    // 重新定义set方法(set,get方法)
    static get factory() {
        return new PointFactory();
    }
}

接下来我们创建Point Factory,并使用我们的工厂

js 复制代码
class PointFactory {
    static new CartesianPoint(x, y) {
        return new Point(x, y)
    }
    
    static newPolarPoint(rho, theta) {
        return new Point(rho + Math.cos(theta), rho * Math.sin(theta))
    }
}

// 如何去使用

let point1 = PointFactory.newPolarPoint(5, Math.PI / 2)
let point2 = PointFactoty.newCartianPoint(5, 6)

console.log(point1, point2)

1.2 抽象工厂:它可以创建家族或组,而不指定它们的具体类型。

维基百科介绍:抽象工厂模式提供了一种方法类封装一组居勇共同主题的单个工厂,而无需指定它们的具体类。

示例如下:使用饮料和饮料制作机原理实现

饮料制作

js 复制代码
class Drink {
    consume() {}
}

// 继承类重写consume
class Tea extends Drink {
    consume() {
        console.log('这是一杯茶')
    }
}

class Coffee extends Drink {
    consume() {
        console.log('这是一杯咖啡')
    }
}

制作饮料工厂

js 复制代码
class DrinkFactory {
    prepare(amount)
}

class TeaFactory extends DrinkFactory {
    makeTea() {
        console.log('Tea Created')
        return new Tea();
    }
}

class CoffeeFactory extends DrinkFactory {
    makeCoffee() {
        console.log('Coffee Created')
        return new Coffee();
    }
}

// 如何使用
// 实例化工厂函数
let teaDrinkFactory = new TeaFactory()
// 调用工厂函数中的方法制作茶
let tea = teaDrinkFactory.makeTea()
// 输出
tea.consume()

1.3 建造者:从简单对象构造复杂对象

维基百科介绍:建造者模式是一种设计模式,为面向对象编程中的各种对象创建问题提供灵活的解决方案。

例子:使用存储个人信息的人员类作为示例

js 复制代码
class Person {
    constructor() {
        this.streetAddress = this.postcode = this.city = ''
        this.companyName = this.position = ''
        this.annualIncome = 0
    }
    
    toString() {
        return (
            `生活在
            ${this.streetAddress}, ${this.city}, ${this.postCode},工作在${this.companyName}是一个${this.position}收入${this.annualIncome}
        )
    }
}

现在创建人员生成器,人员职务生成器,人员地址生成器

js 复制代码
class PersonBuilder {
    constructor(person = new Person()) {
        this.person = person
    }
    
    get lives() {
        return new PersonAddressBuilder();
    }
    
    get works() {
        return new PersonJobBuilder();
    }
    
    build() {
        return this.person
    }
}

// 创建人员工作函数
class PersonJobBuilder extends PersonBuilder {
    constructor(person) {
        super(person);
    }
    
    at(companyName) {
        this.person.companyName = companyName
        return this
    }
    
    asA(position) {
        this.person.position = position
        return this
    }
    
    earning(annualIncome) {
        this.person.annualIncome = annualIncome
        return this
    }
}

// 创建人员地址生成器
class PersonAddressBuilder extends PersonBuilder {
    constructor(person) {
        super(person);
    }
    
    at(streetAddress) {
        this.person.streetAddress = streetAddress
        return this
    }
    
    withPostcode(postcode) {
        this.person.postcode = postcode
        return this
    }
    
    in(city) {
        this.person.city = city
        return this
    }
}

使用

js 复制代码
let personBuilder = new PersonBuilder()
let person = personBuilder.lives
    .at('重庆')
    .in('')
    .withPostCode('9527')
    .works
    .at('重庆')
    asA('')
    .earning('1000000')
    .build()
    
  console.log(person.toString())

1.4 原型:它从现有对象重新创建对象。

维基百科介绍:原型模式是软件开发中的依照那个创建型设计模式,当要创建的对象类型由原型实例决定,会使用原型模式,该实例会被克隆以生成新对象。

js 复制代码
class Car {
    constructor(name, model) {
        this.name = name
        this.model = model
    }
    
    SetName(name) {
        console.log(`${name}`)
    }
    
    clone() {
        return new Car(this.name, this.model)
    }
}

let car = new Car()
car.SetName('奥迪')

// 使用现有对象创建新对象
let car2 = car.clone()
car2.SetName('宝马')

1.5 单例:确保为特定的类只创建一个对象。

维基百科介绍:在软件工程中,单例模式是一种软件设计模式,它将类的实例化限制为一个单一的实例,当只需要一个对象来协调整个系统的操作时,就很有用。

创建一个单例类

js 复制代码
class Singleton {
    constructor() {
        const instance = this.constructor.instance
        if (instance) {
            return instance
        }
        this.constructor.instance = this
    }
    
    say() {
        console.log('Saying...')
    }
} 

let s1 = new Singleton
let s2 = new Singleton

console.log('两者相等吗' + (s1 === s2)) // true

s1.say()

2. 结构设计模式

这些模式涉及类和对象组合,它们使用集成来组合接口

维基百科介绍:在软件工程中,结构设计模式是通过识别实现实体之间关系的简单方法来简化设计的设计模式。

2.1 适配器

这种模式允许具有不兼容接口的类通过将其自己的接口包装在现有的类周围一起工作。

维基百科介绍:在软件工程中,适配器模式是一种软件设计模式,允许将现有类的接口用作另外一个接口,它通常用于使现有类与其他类协同工作而无需修改源码。

例子:

js 复制代码
class Calculator1 {
    constructor() {
        this.operations = function(value1, value2, operation) {
            switch (operation) {
                case 'add': 
                    return value1 + value2
                case 'sub': 
                    return value1 - value2
            }
        }
    }
}

class Calculator2 {
    constructor() {
        this.add = function(value1, value2) {
            return value1 + value2
        }
        this.sub = function(value1, value2) {
           return value1 - value2
        }
    }
}


// 添加适配器
class CalcAdapter {
    constructor() {
        const cal2 = new Calculator2()
        this.operation = function(value1, value2, operation) {
            switch (operation) {
                case 'add': 
                    return cal2.add(value1, value2)
                case 'sub': 
                    return cal2.sub(value1, value2)
            }
        }
    }
}

const adaptedCalc = mew CalcAdapter()
console.log(adaptedCalc.operation(10, 55, 'sub'));

2.2 合成的:它将对象组合起来,以便于将它们作为单个兑现进行操作。

示例:

js 复制代码
class Employer {
    constructor(name, role) {
        this.name = name
        this.role = role
    }
    
    print() {
        console.log('name' + this.name)
    }
}

class EmployerGroup {
    constructor(name, composite=[]) {
        console.log(name)
        this.name = name
        this.composites = composie
    }
    
    print() {
        console.log(this.name)
        this.composites.forEach(item => {
            item.print()
        })
    }
}

let ravi = new Employer('ravi', 'develpoer')
let bhavy = new Employer('bhavy', 'develpoer')
let groupDeveopers = new EmployerGroup('Developer', [ravi, bhavy])

2.3 装饰器:动态的添加或覆盖对象的行为

js 复制代码
class Shape {
    constructor(color) {
        this.color = color
    }
}

class Circle extends Shape {
    constructor(radius = 0) {
        super()
        this.radius = radius
    }
    resize(factor) {
        this.radius = factor
    }
    toString() {
        return `圆的半径${radius}`
    }
}

class ColoredShape extends Shape {
    constructor(shape, color) {
        super()
        this.shape = shape
        this.color = color
    }
    toString() {
        return `${this.shape.toString()}颜色${this.color}`
    }
}

let circle = new Circle(2)

let redCircle = new ColorShape(circle, 'red')

redCircle.toString()

2.4 代理:一个类可以表示另外一个类的功能

js 复制代码
class Percentage {
    constructor(percent) {
        this.percent = percent
    }
    toString() {
        return `${this.percent}`
    }
    valueOf() {
        return this.percent / 100
    }
}

let fivePercent = new Percentage(5)

fivePercent.toString()
console.log(50 * fivePentent)

3.行为设计模式

行为设计模式特点就是关注对象之间的通信。

3.1 迭代器:访问对象的元素而不暴露其底层表示。

示例:以数组为例

js 复制代码
class Stuff {
    constructor() {
        this.a = 11
        this.b = 22
    }
    
    [Symbol.iterator]() {
        let i = 0'
        let self = this
        return (
            next: function() {
                return {
                    done: i > 1,
                    value: self[t++ === 0 ? 'a' : 'b']
                }
            }
        )
    }
    
    get backwards() {
        let i = 0;
        let self = this
         return (
            next: function() {
                return {
                    done: i > 1,
                    value: self[t++ === 0 ? 'b' : 'a']
                }
            },
            [Symbol.iterator]: function() {return this}
        )
    }
}

如何使用

js 复制代码
let values = [100, 200, 300]

for (let i in values) {
    console.log(i, values[i])
}

let stuff = new Stuff()

for (let item of stuff)
    console.log(item)
for (let item of stuff.backwards) 
    console.log(item)

3.2 调解员:第三方对象控制两个对象之间的交互

js 复制代码
class Person {
    consructor(name) {
        this.name = name
        this.chatLog = []
    }
    
    receive(sender, message) {
        let s = `${sender}: ${message}`
        console.log(`[${this.name}发送信息${s}]`)
        this.chatLog.push(s)
    }
    
    say(message) {
        this.room.broadcast(this.name, message)
    }
    pm(who, message) {
        this.room.message(this.name, who, message)
    }
}

创建聊天室

js 复制代码
class ChatRoom {
    constructor() {
        this.people = []
    }
    broadcast(source, message) {
        for (let p of this.people) {
            if (p.name !== source) {
                p.receive(source, message)
            }
        }
    }
    
    join(p) {
        let joinMsg = `${p.name} 加入聊天室`
        this.broadcast('room', joinMsg)
        p.room = this
        this.people.join(p)
    }
    message(source, destination, messge) {
        for (let p of this.people) {
            if(p.name == destination) p.receive(source, message)
        }
    }
}

使用

js 复制代码
let room = new ChatRoom()

let person1 = new Person('张三')
let person2 = new Person('李四')

room.join(person1)
room.join(person2)

person1.say('hello')

let person3 = new Person('王五')
room.join(person3)
person.say('我叫王五')

3.3 观察者:允许多个观察者对象控制一个事件。

js 复制代码
class Event {
    constructor() {
        this.handlers = new Map()
        this.count = 0
    }
    subscribe(handler) {
        this.handler.set(++this.count, handler)
        return this.count
    }
    unsubscribe(idx) {
        this.handlers.delete(idx)
    }
    fire(sender, args) {
        this.handler.forEach((v, k) => {v(sender, args)})
    }
}

class FallsIllArgs {
    constructor(address) {
        this.address = address
        this.fallsIll = new Event()
    }
    catchCold() {
        this.fallsIll.fire(this, new FallsIllArgs(this.address))
    }
}

使用

js 复制代码
let person new Person('ABC road')
let sub = person.fallsIll.subscribe((s, a) => {
    console.log(`医生被呼叫到${a.address}}`)
})

person.catchCold()
person.catchCold()

person.fallsIll.unsubscribe(sub)
person.catchCold()

结语:我们在遇到某些场景时,虽然可以凭借其他方案解决问题,但是也可以尝试使用设计模式的思路去实现,这样不仅可以提升代码的可读性,同时也开阔了自己的思维,不会局限于一种解决方案。虽然使用了设计模式看起来代码量提升了,还会觉得写那么多,都是重复的,确实,但是也不妨碍它能解决一些复杂的问题。

ps: 其中某些本人用过,某些是参考,如有雷同,纯属巧合。

相关推荐
晨米酱5 小时前
JavaScript 中"对象即函数"设计模式
前端·设计模式
数据智能老司机10 小时前
精通 Python 设计模式——分布式系统模式
python·设计模式·架构
数据智能老司机11 小时前
精通 Python 设计模式——并发与异步模式
python·设计模式·编程语言
数据智能老司机11 小时前
精通 Python 设计模式——测试模式
python·设计模式·架构
数据智能老司机11 小时前
精通 Python 设计模式——性能模式
python·设计模式·架构
使一颗心免于哀伤12 小时前
《设计模式之禅》笔记摘录 - 21.状态模式
笔记·设计模式
数据智能老司机1 天前
精通 Python 设计模式——创建型设计模式
python·设计模式·架构
数据智能老司机1 天前
精通 Python 设计模式——SOLID 原则
python·设计模式·架构
烛阴1 天前
【TS 设计模式完全指南】懒加载、缓存与权限控制:代理模式在 TypeScript 中的三大妙用
javascript·设计模式·typescript
李广坤1 天前
工厂模式
设计模式