聊一聊面试中经常被问到的设计模式

哈喽大家好,我是Fine。

设计模式在前端开发中起着重要的作用,它们提供了一种经过验证的解决方案,帮助我们解决常见的软件设计问题。举个栗子,Vue框架在其设计和实现中使用了多种设计模式:观察者模式、装饰者模式、适配器模式等,你能举出这些模式在Vue中的应用场景吗?

今天来分享一篇通俗易懂的文章来介绍设计模式,以下是正文:

前言

最近在面试的时候,基本上都会提一嘴设计模式,但是被问到的面试者大多数人总感觉在背题,没有从项目的角度,或者说从实际的项目开发中来说这个事情,借此机会用大白话总结一下日常接触到的那些设计模式

设计模式的定义

通常的定义如下:
设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的
换句话说就是,先有代码,然后大佬们针对特定场景总结出通用解决方案,而这些通用的解决方案就是设计模式

日常工作中经常遇到的设计模式

单例模式

全局唯一的对象或方法
使用场景:项目中公用的类通过export default将实例导出来实现全局唯一

class CommonClass{
...code
}
export default new CommonClass()

工厂模式

批量创建相同类型的对象
使用场景:react ReactDOM.createRoot React.createElement

react中最常见的jsx,本质就是React.createElement,而React.createElement就是工厂模式的实现,它可以批量创建 React 元素,在老版的官网中甚至都直接说出这个就是工厂模式

image.png

代理模式

为其他对象提供一种代理以控制对这个对象的访问 使用场景 vue2中的Object.defineProperty,vue3中的proxy,微信小程序中使用的较多 数据代理 惰性取值等 如:在小程序中通常都会有埋点操作,因为是通用方法,所以在app.js中引入,但是此时在初始化app的时候,是拿不到app,那么我们就可以借助代理模式来实现惰性取值

const app = {};
Object.defineProperty(app, 'sensors', {
    get() {
        return getApp().sensors;
    }
})

又或者图片的懒加载

var myImage = (function(){
    var imgNode = document.createElement('img');
    document.body.appendChild(imgNode);
    return {
        setSrcfunction(src{
            imgNode.src = src;
        }
    }
})();

var proxyImage = (function(){
    var img = new Image;
    img.onload = function({
        myImage.setSrc(this.src);
    }
    return {
        setSrcfunction(src{
            myImage.setSrc('./preset.png');  // 一般是本地统一的占位图
            img.src = src;
        }
    }
})();

proxyImage.setSrc('./real.png');  // 一般是异步获取的图片链接

装饰器模式

装饰器模式允许向一个现有的对象添加新的功能,同时又不改变其结构。
主打的就是在外面加一层
项目中的组件复用,基本都是利用的这种模式
再比如在执行函数执行之前,添加其他的执行逻辑

/** 包裹执行函数,在执行函数执行之前,添加执行逻辑 */
const funcWrapper = (cb) => (...params) => {
    // 在执行函数之前,执行其他逻辑
    return cb(...params);
}

策略模式

策略模式就是根据不同参数可以命中不同的策略 简单的说就是每个key都对应一个策略(场景),这样我们很容易想到对象的写法
使用场景 订单状态不同,处理方式不同

const ORDERSTRATEGIE={
    orderStatus1()=>{
        console.log(`当订单状态为${orderStatus1}时的处理逻辑`)
    },
    orderStatus2()=>{
        console.log(`当订单状态为${orderStatus2}时的处理逻辑`)
    },
    orderStatus3()=>{
        console.log(`当订单状态为${orderStatus3}时的处理逻辑`)
    },
    ...code
}

适配器模式

常见于跨端中,主打的就是适配
使用场景 pc端需要h5端填写信息确认,下拉组件需要在pc和h5有着不同的表现
和策略模式的区别在于,适配器模式需要依赖现有的代码,如上述场景中pc项目的代码不用动,在此基础上添加适配h5的代码即可

functino Select(props){
    if(isH5){
        return <H5Select {...props} />
    }
    // 原有的代码无需改动
    return <PCSelect {...props} />
}

迭代器模式

迭代器模式提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示
这个我们可以依赖es6 中的提供的api iterator 即可,毕竟实际开发中确实很少遇到😂

发布订阅模式

发布-订阅模式其实是一种对象间一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到状态改变 使用场景如微信小程序的事件触发,vue的EventBus

class Event {
    eventList = [];
    on(cb) {
        this.eventList.push(cb);
    }
    emit() {
        this.eventList.forEach(cb => cb());
    }
    off() {
        this.eventList.length=0
    }
}

组合模式

组合模式将对象组合成树形结构,以表示“部分-整体”的层次结构
表单控件是复合模式的一个完美例子。树的节点要么包含一个单独的组件(对象),要么包含一组组件(对象)

以下是antd的表单代码

import React from 'react'
import { Button, Checkbox, Form, Input } from 'antd'

const onFinish = (values) => { console.log('Success:', values); }; 
const onFinishFailed = (errorInfo) => { console.log('Failed:', errorInfo); }; 

const App = () => ( 
<Form 
    initialValues={{ remember: true, }} 
    onFinish={onFinish} 
    onFinishFailed={onFinishFailed} 
    autoComplete="off"
>
 
    <Form.Item
        label="Username" 
        name="username" 
        rules={[ { required: truemessage: 'Please input your username!', }, ]} 
    >
 
        <Input /> 
    </Form.Item> 
    <Form.Item 
        label="Password" 
        name="password" 
        rules={[ { required: truemessage: 'Please input your password!', }, ]} 
    >
 
        <Input.Password /> 
    </Form.Item> 
    <Form.Item 
        name="remember" 
        valuePropName="checked" 
    >
 
        <Checkbox>Remember me</Checkbox> 
    </Form.Item> 
    <Form.Item> 
        <Button type="primary" htmlType="submit"> 
        Submit
        </Button> 
    </Form.Item> 
</Form>
 ); 

原型模式

原型模式允许我们轻松地让对象访问和继承其他对象的属性。
由于原型链允许我们访问未直接定义在对象本身上的属性,我们可以避免方法和属性的重复,从而减少使用的内存量
最常见的是vue2, 在vue的prototype上添加项目中公用的方法,变量

import axios from 'axios'
...code

Vue.prototype.$http = axios

...code

中介者模式

中介者模式限制对象之间的直接交互, 迫使它们通过一个中介者对象进行合作

比如各种状态管理库,react的redux,mobx,vue的pinia,降低了组件间的耦合度

职责链模式

职责链模式使多个对象(方法)都有机会处理请求,将这些对象(方法)连成一条链,并沿着这条链传递该请求

最常见的是dom中的事件冒泡

    <body>
        <div class="out" onclick="clickOut()">
            <div class="middle" onclick="clickMiddle()">
                <div class="inner" onclick="clickInner()"></div>
            </div>
        </div>
        <script>
            function clickOut({
                console.log("点击了外部元素");
            }
            function clickMiddle({
                console.log("点击了中间元素");
            }
            function clickInner({
                console.log("点击了内部元素");
            }
        
</script>
    </body>
image.png

以上就是我关于日常开发中常遇到的设计模式的大白话总结

原文地址:https://juejin.cn/post/7350917259719376923

来源掘金,侵删

最后

还没有使用过我们刷题网站(https://fe.ecool.fun/)或者刷题小程序的同学,如果近期准备或者正在找工作,千万不要错过,题库主打无广告和更新快哦~。

老规矩,也给我们团队的辅导服务打个广告。