同步模块模式

同步模块模式

同步模块模式SMD是请求发出后,无论模块是否存在,立即执行后续的逻辑,实现模块开发中对模块的立即引用,模块化是将复杂的系统分解为高内聚、低耦合模块,同步模块模式不属于一般定义的23种设计模式的范畴,而通常将其看作广义上的架构型设计模式。

描述#

同步模块模式通常用来解决如下场景的问题,随着页面功能的增加,系统的业务逻辑越来越复杂,多人开发的功能经常耦合在一起,有时项目经理提出的需求,分配给多人实现的时候,常常因为某一处功能耦合了多人的代码,从而出现排队修改的现象。
通过使用模块化来分解复杂的系统可以很好的去解决这个问题,要想实现模块化开发,首先就需要有一个模块管理器,其管理着模块的创建与调度,对于模块的调用分为两类,第一类就是同步的模块调度,实现相对比较简单,不需要考虑模块间的异步加载,第二类的异步模块调度就比较繁琐,其可以实现对模块的加载调度。

实现#

Copy
// 定义模块管理器单体对象 var F = F || {}; // 创建模块的方法define // str 模块路由; fn 模块方法 F.define = function(str, fn){ // 定义模块方法,本应该在闭包中定义,这里先忽略 let parts = str.split("."); // 解析模块路由str // 如果在闭包中,为了屏蔽对模块的直接访问,建议将模块添加给闭包内部私有变量 // old,当前模块的祖父模块;parent,当前模块父模块 let old = this; let parent = this; // i模块层级,len模块层级长度 let i = 0; // 如果第一个模块是模块管理器单体对象,则移除 if(parts[0] === "F") parts = parts.slice(1); // 屏蔽对define与module模块方法的重写 if(parts[0] === "define" || parts[0] === "module") return void 0; // 遍历路由器并定义每层模块 for(let len = parts.length; i < len; i++){ // 如果父模块中不存在当前模块,声明当前模块 if(parent[parts[i]] === void 0) parent[parts[i]] = {}; // 缓存下一级的祖父模块 old = parent; // 缓存下一级的父模块 parent = parent[parts[i]]; } // 如果给定模块方法fn则定义改模块方法 if(fn){ // 此时i等于parts.length,故减1 old[parts[--i]] = fn(); } return this; // 返回模块管理器单体对象 } // 用上面的方法来创建模块 // 创建模块k,并对该模块提供t方法 F.define("k", function(){ return { t: function(){ console.log("it is function t") } } //也可以以构造函数的方法返回 /* let xx = function(){}; xx.t = function(){ console.log("this is xx.t") } xx.tt = function(){ console.log("this is xx.tt") } return xx; */ }); // 使用t方法,但正式的模块开发不允许直接调用 // 一是因为模块通常为闭包中的私有变量,不会保存在F上,获取不到,这里简化没有使用闭包 // 二是因为这样调用不符合模块开发规范 F.k.t(); // 用构造函数返回时的调用方法 /* F.k.t(); F.k.tt(); */ // 也可先声明模块再定义方法 F.define("a.b") F.a.b = function(){ console.log("this is function from a.b") } F.a.b(); // 由于不能直接调用,就需要调用模块的方法 // 调用模块的方法module // 参数分两部分,依赖模块与回调函数(最后一个参数) // 原理是遍历获取所有依赖模块,并保存在依赖模块列表中,然后将这些依赖模块作为参数传入执行函数中执行 F.module = function(...args){ let fn = args.pop(); // 获取回调执行函数 // 获取依赖模块,若args[0]是数组,则它为依赖模块,否则为args let parts = args[0] && args[0] instanceof Array ? args[0] : args; let modules = []; // 依赖模块列表 let modIDs = ""; // 模块路由 let i = 0; // 依赖模块索引 let ilen = parts.length; // 依赖模块长度 // 遍历依赖模块 parts.forEach(v => { if(typeof v === "string"){ // 如果是模块路由 let parent = this; // 设置当前模块父对象(F) // 解析模块路由,并屏蔽掉模块父对象 modIDs = v.replace(/^F./, "").split("."); // 遍历模块路由层级 for(let j = 0; j < modIDs.length; j++){ parent = parent[modIDs[j]] || false; // 重置父模块 } modules.push(parent); // 将模块添加到模块依赖列表 }else{ // 如果是模块对象 modules.push(v); // 直接加入模块依赖列表 } }) fn.apply(null, modules); // 执行回调函数 } // 依赖dom和k模块的方法,数组形式 F.module(["dom", "k"], function(){ console.log(1); }) // 依赖dom2模块和k.a方法,字符串形式 F.module("dom2", "k.a", function(){ console.log(2); })

每日一题#

Copy
https://github.com/WindrunnerMax/EveryDay

参考#

Copy
https://www.jianshu.com/p/2359390737aa https://www.dazhuanlan.com/2020/03/09/5e65fa05c9bb7/ https://blog.csdn.net/WuLex/article/details/107350493
posted @   WindRunnerMax  阅读(176)  评论(0编辑  收藏  举报
编辑推荐:
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
阅读排行:
· 终于写完轮子一部分:tcp代理 了,记录一下
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
点击右上角即可分享
微信分享提示
CONTENTS