常用设计模式(三)

news2025/2/13 12:45:59

接着之前的思路今天来介绍一下常用的设计模式有哪些

单例模式(Singleton Pattern)

又称为单体模式,保证一个类只有一个实例,并提供一个访问它的全局访问点。也就是说,第二次使用同一个类创建新对象的时候,应该得到与第一次创建的对象完全相同的对象。
比如说常见的前端的 window、document、Redux数据仓库store等…这些都是很典型的单例模式。
再比如前端常用的jquery、axios对外暴露的都是全局唯一的一个实例。

通过静态属性创建单例

class Person{
    static instance = null;
    constructor(name){
        if(Person.instance){
            return Person.instance;
        }
        Person.instance = this;
        this.name = name;
    }
}

let zhangsan = new Person("张三")
let lisi = new Person("李四")

// 虽然示例话了两次,但返回的实际是全局内的同一个
console.log(zhangsan, lisi)
console.log(zhangsan===lisi);

在这里插入图片描述

通用单例

上面的方法可以,但不够通用。比如此时有一个Person, 一个Animal的话,那就需要在每个类中都定义这么个静态属性来判断。
此时我们就可以使用工厂模式来封装判断一下

class Person{
    constructor(name){
        this.name = name;
    }
}

// 通用单例,通过闭包判断是否有已近有实例存在了
function createInstance(fn){
    let instance;
    return function(...args){
        if(!instance){
            instance = new fn(...args);
        }
        return instance
    }
}

let singlePerson = createInstance(Perosn);
let zhansan  = new singlePerson("张三");
let lisi  = new singlePerson("李四");
console.log(zhangsan, lisi)
console.log(zhansan===lisi);

在这里插入图片描述
可以看到此时的效果是一样的,但这样更加的通用,我们吧单利的判断逻辑单独的抽离出去。正常需要使用单例的class只需要通过我们提供的函数包装一下即可。

工厂模式

工厂模式 (Factory Pattern),封装具体实例创建逻辑和过程,外部只需要根据不同条件返回不同的实例。
对象的创建和实现做对应的分离,解耦了实现和创建。
可以类比下取货是从工厂取货的,货物的实现是工厂里面的工人在另外一个地方(另外的函数/类/对象)中实现的
● 优点:实现代码复用性,封装良好,抽象逻辑;
● 缺点:增加了代码复杂程度;
比如下面两个Person和Animal两个类。两个类的方法/属性都在各自的类中

class Person {
	constructor(name) {
    	this.name = name;
  }
}
class Animal {
	constructor(name) {
    	this.name = name;
  }
}

可以用一个工厂类封装类实例的创建过程, 相当于用Factory来实例出对象,把上面功能的实现给抽离出来。

function Factory(name) {
  //使用者只需要关注怎么创建/使用实例即可,具体的类的内部实现不关心 
  switch (name) {
    case 'person':
      return new Person();
      break;
    case 'animal':
      return new Animal();
      break;
    default:
      console.log("无...");
      break;
  }
}
 const person = Factory("person");
 const animal = Factory("animal");
 console.log(person,animal);

装饰者模式

装饰者模式 (Decorator Pattern)使用一种更为灵活的方式来动态给一个对象/函数等添加额外信息/功能
● 扩展功能和继承类似
● 扩展不同类的功能,和原始类并无关联;
缺点:要装饰的对象不清楚
其实就是功能的扩展,以类举例,在类中可以使用extend来继承,extend很多情况下都是在基类中去扩展一些新的功能
而装饰着模式,都是拓展一些额外的功能。
比如看下面这个demo

class Person{
    constructor(){
        this.name = "张三";
    }
    skill(){
        console.log("张三有技能.");
    }
}

let zhansan = new Person();
zhansan.skill();

function level(){
    console.log("软件开发架构师");
}
function years(){
    console.log("工作了8年");
}

Function.prototype.Decorator = function(fn){
    // 谁调用了这个Function那么this就指向谁
    let _this = this;
    // 返回一个函数(函数原型链上有Decorator)
    // 这样后面就可以接着使用Decorator了,就形成了装饰者链
    return function(){
        // yase.release()这个方法要执行
        _this(); 
        // 同时也要拓展/执行传递进来的方法
        fn();
    }
}
zhansan.skill.Decorator(level)();
// 装饰者链
zhansan.skill.Decorator(level).Decorator(years)();

定义了一个Person的类,有技能。但是我们期望在有技能的基础上新增一些功能,比如工作的年限,以及当前的水平是如何。
我们就可以使用装饰着模式来增加Person的功能
最后一次打印的日志如下:
在这里插入图片描述

观察者模式

观察者模式 (Observer Pattern) 定义一个对象与其他对象之间的一种依赖关系,当对象发生某种变化的时候,依赖它的其它对象都会得到更新。
优点:

  1. 把一个对象和另一个对象关联在一起
  2. 可以惰性执行,比如回调函数,并不会立即执行,而是被观察的对象相应的事件触发之后才会执行
  3. 可以一对多的关系

意义:做解耦,把两个类,两个对象,两个模块通过事件管理,统一去管理事件,把事件解耦

这个在平时开发中其实就有很多的应用场景。比如下面其实就是把.box这个元素观察起来,如果这个元素被点击了,就会触发对应的回调

document.querySelector(".box").addEventListener("click",function(){
    console.log("click1");
})
// 一对多的注册也ok
document.querySelector(".box").addEventListener("click",function(){
  console.log("click1");
})
document.querySelector(".box").addEventListener("click",function(){
  console.log("click2");
})

基本实现

// 先定义两个对象,将这两个对象关联起来
let obj1 = {
    fn1(){
        console.log("fn1更新");
    }
}
let obj2 = {
    fn2(){
        console.log("fn2更新");
    }
}

// 管理事件类
class MyEvent{
    constructor(){
        this.handles = {};
    }
    addEvent(eventName,fn){
        // {myevent1:[fn1,fn2...],myevent2:[fn1,fn2...]}
        if(typeof this.handles[eventName]==="undefined"){
            // 第一次没有,先初始化为一个数组
            this.handles[eventName] = [];
        }
        // 该事件对应的有一个数组事件
        this.handles[eventName].push(fn);
    }
    trigger(eventName){
        // 当前事件不在handles里面
        if(!(eventName in this.handles)){
            return ;
        }
        // 监听该事件的数组函数全部执行一遍
        this.handles[eventName].forEach(fn=>{
            fn();
        })
    }
}

// 使用
let eventObj = new MyEvent();
eventObj.addEvent("myevent",obj1.fn1);
eventObj.addEvent("myevent",obj2.fn2);

setTimeout(()=>{
    eventObj.trigger("myevent")
},1000)

代理模式

代理模式为其他对象提供一种代理以控制对这个对象的访问,类似于生活中的中介。需要对原本代理的对象做一些控制,如果不做控制的话,那就没有多大意义了

应用

  1. proxy 服务器代理 转发请求 nginx
  2. ES6 的Proxy,可以代理对象,对原本的对象做一些控制(VUE3的核心)

Demo

我们可以代理图片的展示,以实现图片懒加载的效果

class CreateImage{
    constructor(){
        this.img = document.createElement("img");
        document.body.appendChild(this.img);
    }
    setSrc(src){
        this.img.src = src;
    }
}
// 目标图片,
let src = "http2://x.x.xx.xxx";
// 代理图片的创建
function proxyImg(src){
    let myImg = new CreateImage();
    // 创建一个Img加载目标图片
    let loadImg = new Image();
    // 本地图片,加载快速
    myImg.setSrc("./loading.gif");
    // 创建的loadImg真实加载目标图片
    loadImg.src = src;
    // 目标图片加载到本地之后,再修改myImg的src
    loadImg.onload = function(){
        myImg.setSrc(src);
    }
}
proxyImg(src);

适配器模式

两个不兼容的接口之间的桥梁,将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
类似举个例子比如中国电压220v去欧洲不能正常充电,就需要一个转换器来做适配

我们在编码时最好不要过多的使用,过多的使用就会显得比较凌乱,要适度使用,使用好。
在前端axios兼容前端浏览器和node,实际上就是使用了适配器。里面有个模块专门做适配的

// 适配器模式
function getUsers(){
    return [{
        name:"yjian",
        age:21
    },{
        name:"cyril",
        age:26
    }]
}
// 如果期望返回的: // [{yjian:21},{cyril,26}]

function Adaptor(users){
    let arr = [];
    for(let i=0;i<users.length;i++){
        arr[users[i].name] = users[i].age;
    }
    return arr;
}

let res =  Adaptor(getUsers());
console.log(res);

mixin 混入模式

也是为了扩展原有对象的功能。vue2里面用的比较多

// 混入模式

// 工程师
class FrontEngineer{
    constructor(){
        this.name = "工程师";
    }
}

// 工程师的技能单独抽成一个类(除了前端工程师,其他工程师也是有技能的)
class Skills{
    code(){
        console.log("编写代码")
    }
    architect(){
        console.log("技术架构能力");
    }
    http(){
        console.log("了解http协议");
    }
}

// 目标:实例化一个工程师,工程师有技能

// 可以使用集成 比如 Skill extend FrontEngineer.
// 那技能继承工程师,当前场景需求虽然满足,但会觉得非常奇怪,技能继承于职位。而且其他职位也有技能
// js语言里面没有多继承的方式,即一个类即继承FrontEngineer,又继承Skills
// 所以就需要这种混入模式
// let engineer = new FrontEngineer();
// 混入模式,思路是 Class其实都是函数,在原型链上找到技能的函数强行混到FrontEngineer的原型链即可
function mixin(receivingClass,givingClass){
	// 后面的参数就是需要混入的函数名
    if(typeof arguments[2] !== "undefined"){
        for(let i=2;i<arguments.length;i++){
            receivingClass.prototype[arguments[i]]= givingClass.prototype[arguments[i]];
        }
    }
}

// 把Skills中的code, architect, http技能给混入到FrontEngineer中去
mixin(FrontEngineer,Skills,"code","architect","http");

let engineer = new FrontEngineer();
console.log(engineer);
engineer.architect();

在这里插入图片描述
可以看到在engineer的原型链上已近混入了code、architect、http方法

享元模式

享元模式:运用共享技术来有效地支持对象的复用,以减少创建的对象的数量。
通过共享对象节约内存资源,提高性能和效率

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/639511.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

机器学习-8 集成学习

集成学习 集成学习理论集成学习概述集成学习理论 随机森林算法原理Scikit-learn中的随机森林分类器函数原型sklearn.ensemble.RandomForestClassifier(n_estimators10,criteriongini,max_depthNone,min_samples_split2,min_samples_leaf1,min_weight_fraction_leaf0.0,max_feat…

pytorch深度学习框架—torch.nn模块(一)

pytorch深度学习框架—torch.nn模块 torch.nn模块中包括了pytorch中已经准备好的层&#xff0c;方便使用者调用构建的网络。包括了卷积层&#xff0c;池化层&#xff0c;激活函数层&#xff0c;循环层&#xff0c;全连接层。 卷积层 pytorch中针对使用场景的不同&#xff0c;有…

多云时代来临,容器存储谁敢横刀立马?

多云时代来临&#xff0c;容器存储谁敢横刀立马&#xff1f; 原创2023-06-12 21:54大数据在线 2011年&#xff0c;Gartner提出了数字化转型的理念&#xff1b;十多年来&#xff0c;企业数字化经历了移动化、云化等几次变革潮流&#xff0c;如今走入一个新的时代&#xff1a;多…

Docker 生成 golang 程序镜像

一. 准备golang程序 package mainimport "github.com/gin-gonic/gin"func main() {go func() {r1 : gin.Default()r1.GET("/sayHello/", func(c *gin.Context) {c.JSON(200, "hello1")})r1.Run(":8992")}()r : gin.Default()r.GET(&q…

详解Linux内核安全技术——磁盘加密技术概述和eCryptfs

一、概述 加密是最常见的数据安全保护技术&#xff0c;在数据生命周期各阶段均有应用。从应用场景和技术实现上&#xff0c;按加密对象、用户是否感知、加密算法等维度&#xff0c;有多种分类及对应方案&#xff0c;并在主流操作系统如Windows、Linux、Android中有广泛应用。 …

AWS CodeWhisperer 简单介绍

一、何为AWS CodeWhisperer Amazon CodeWhisperer能够理解以自然语言&#xff08;英语&#xff09;编写的注释&#xff0c;并能实时生成多条代码建议&#xff0c; 以此提高开发人员生产力。 二、主要功能 Amazon CodeWhisperer 的主要功能&#xff0c;包括代码生成、引用追踪…

12.异常检测

12.1 异常检测的应用 异常检测最常见的应用是欺诈检测&#xff1b; 如果你有很多用户&#xff0c;每个用户都在从事不同的的活动&#xff0c;你可以对不同的用户活动计算特征变量&#xff0c;然后可以建立一个模型来表示用户表现出各种行为的可能性&#xff0c;用来表示用户行…

LiangGaRy-学习笔记-Day22

1、shell工具-tput 这个是tput bash工具 具体的操作如下&#xff1a; tput clear&#xff1a;清屏tput cup Y X 第Y行&#xff0c;第X列的位置 tput bold&#xff1a;字体加粗tput sgr0 &#xff1a; 重置命令tput setaf n n&#xff1a;代表数字0-7 0黑色1红色2绿色3黄色4蓝…

【Protobuf速成指南】Map类型的使用

文章目录 2.4 map 类型一、 基本认识二、map相关函数4.3 contact2.4的改写 2.4 map 类型 本系列文章将通过对通讯录项目的不断完善&#xff0c;带大家由浅入深的学习Protobuf的使用。这是Contacts的2.4版本&#xff0c;在这篇文章中将带大家学习Protobuf的 map 语法&#xff0c…

Java自制绘图软件

引言 在大连时接触了Java&#xff0c;Java非常优雅&#xff0c;我非常喜欢。期末老师要做一个项目作品&#xff0c;看着windows自带的画图软件&#xff0c;我决定自己动手做一个绘图软件。经过无数次界面调试&#xff0c;刷新层叠关系令我爆肝了一个月&#xff0c;最终完成了这…

没有AI歌手一键包,纯纯的算法项目

项目地址&#xff1a; https://github.com/PlayVoice/so-vits-svc-5.0 来源丨元语音技术 本项目的目标群体是&#xff1a;深度学习初学者&#xff0c;具备Pyhon和PyTorch的基本操作是使用本项目的前置条件&#xff1b; 本项目旨在帮助深度学习初学者&#xff0c;摆脱枯燥的纯…

2023年6月最新|大屏可视化

大屏可视化 一、可视化适配 大屏下显示一般都是16:9尺寸 1920*1080 &#xff0c;做适配也就是在这个比例的基础上进行的 方案一&#xff1a;打开VSCode终端&#xff0c;下载flexible npm i lib-flexible修改源码 要修改的源码的路径&#xff1a; 项目/mode_modules/lib-fl…

Springboot+Vue服务器盲盒活动

文章目录 一、项目要求二、说明文档1、用户抽奖主页/raffle2、多种奖品链接1、奖品1 discont /discount2、奖品2 CPU upgrade /cpu3、奖品3 Memory upgrade /memory4、奖品4 Increase duration /duration5、奖品5 Send to server /server6、奖品6 Configuration upgrade /upgra…

信必优成功案例-全球Tops汽车技术供应商

该集团是全球Tops汽车技术供应商&#xff0c;业务范围涵盖了汽油系统、柴油系统、汽车底盘控制系统、汽车电子驱动、起动机与发电机、电动工具、家用电器、传动与控制技术、热力技术和安防系统等。信必优于2021年开始为该客户服务&#xff0c;参与了客户多个项目&#xff0c;以…

Android进阶 四大组件的工作过程(二):Service的工作过程

Android进阶 四大组件的工作过程&#xff08;二&#xff09;&#xff1a;Service的工作过程 导语 本片文章主要是来介绍Service组件的工作过程的&#xff0c;主要分成Service的启动和Service的绑定两个部分来讲。上一篇文章我们已经介绍了Activity的工作工程&#xff0c;而Ser…

Windows软件:如何安装ideaIU-2020.3.3并使用无限重置插件达到永久使用

​前言&#xff1a; ​在我们做SpringBoot Java项目时&#xff0c;一个好的开发工具可以让我们的效率达到事半功倍&#xff0c;早期的eclipse已经无法满足我们日常需求&#xff0c;当然idea集成开发工具在很早前就已经进入市场&#xff0c;如今取得了无法撼动的地位&#xff0c…

度学习pytorch实战六:ResNet50网络图像分类篇自建花数据集图像分类(5类)超详细代码

1.数据集简介、训练集与测试集划分 2.模型相关知识 3.model.py——定义ResNet50网络模型 4.train.py——加载数据集并训练&#xff0c;训练集计算损失值loss&#xff0c;测试集计算accuracy&#xff0c;保存训练好的网络参数 5.predict.py——利用训练好的网络参数后&#xff0…

为Nomad Web使用添加快捷应用图标

大家好&#xff0c;才是真的好。 上次我们说到Nomad Web已经更新到了1.0.8版本&#xff0c;作为未来的“Notes客户机”&#xff08;运行在浏览器中&#xff09;&#xff0c;Nomad Web的更新迭代很快。从1.0.5版本开始&#xff0c;就可以直接部署在Domino服务器上&#xff0c;而…

STM32MP157_PRO开发板的第一个驱动程序

文章目录 目的&#xff1a;为什么编译驱动程序之前要先编译内核&#xff1f;编译内核编译设备树编译安装内核模块编译内核模块安装内核模块到 Ubuntu 的NFS目录下备用 安装内核和模块到开发板上编译 led 驱动在开发板安装驱动模块下载驱动程序安装驱动模块 目的&#xff1a; 在…

Netty实战(十三)

WebSocket协议&#xff08;一&#xff09; 一、什么是WebSocket 协议二、简单的 WebSocket 程序示例2.1 程序逻辑2.2 添加 WebSocket 支持2.3 处理 HTTP 请求2.4 处理 WebSocket 帧 一、什么是WebSocket 协议 WebSocket 协议是完全重新设计的协议&#xff0c;旨在为 Web 上的双…