软件架构设计案例_透过现象看本质:常见的前端架构风格和案例

所谓软件架构风格,是指描述某个特定应用领域中系统组织方式的惯用模式。架构风格定义一个词汇表和一组约束,词汇表中包含一些组件及连接器,约束则指出系统如何将构建和连接器组合起来。软件架构风格反映了领域中众多系统所共有的结构和语义特性,并指导如何将系统中的各个模块和子系统有机的结合为一个完整的系统

没多少人能记住上面的定义,需要注意的是本文不是专业讨论系统架构的文章,笔者也还没到那个水平. 所以暂时没必要纠结于什么是架构模式、什么是架构风格。在这里尚且把它们都当成一个系统架构上的套路, 所谓的套路就是一些通用的、可复用的,用于应对某类问题的方式方法. 可以理解为类似“设计模式”的东西,只是解决问题的层次不一样

透过现象看本质,本文将带你领略前端领域一些流行技术栈背后的架构思想。直接进入正题吧

文章大纲

  • 分层风格
  • Virtual DOM
  • Taro
  • 管道和过滤器
  • 中间件(Middleware)
  • 事件驱动
  • MV*
  • 家喻户晓的MVC
  • Redux
  • 复制风格
  • 微内核架构
  • 微前端
  • 组件化架构
  • 其他
  • 扩展阅读

分层风格

没有什么问题是分层解决不了,如果解决不了, 就再加一层 —— 鲁迅 不不,原话是: Any problem in computer science can be solved by anther layer of indirection.

分层架构是最常见的软件架构,你要不知道用什么架构,或者不知道怎么解决问题,那就尝试加多一层。

一个分层系统是按照层次来组织的,每一层为在其之上的层提供服务,并且使用在其之下的层所提供的服务. 分层通常可以解决什么问题

  • 是隔离业务复杂度与技术复杂度的利器. 典型的例子是网络协议, 越高层越面向人类,越底层越面向机器。一层一层往上,很多技术的细节都被隐藏了,比如我们使用HTTP时,不需要考虑TCP层的握手和包传输细节,TCP层不需要关心IP层的寻址和路由。
6e1a4be8150f7cccbf9401749b18ece8.png
  • 分离关注点和复用。减少跨越多层的耦合, 当一层变动时不会影响到其他层。例如我们前端项目建议拆分逻辑层和视图层,一方面可以降低逻辑和视图之间的耦合,当视图层元素变动时可以尽量减少对逻辑层的影响;另外一个好处是, 当逻辑抽取出去后,可以被不同平台的视图复用。

关注点分离之后,软件的结构会变得容易理解和开发, 每一层可以被复用, 容易被测试, 其他层的接口通过模拟解决. 但是分层架构,也不是全是优点,分层的抽象可能会丢失部分效率和灵活性, 比如编程语言就有'层次'(此例可能不太严谨),语言抽象的层次越高,一般运行效率可能会有所衰减:

024da1806cc306a81c7d270c3654d75a.png

分层架构在软件领域的案例实在太多太多了,咱讲讲前端的一些'分层'案例:

我自己是一名从事了多年开发的web前端老程序员,目前辞职在做自己的web前端私人定制课程,今年年初我花了一个月整理了一份最适合2019年学习的web前端学习干货,各种框架都有整理,送给每一位前端小伙伴,想要获取的可以关注我的头条号并在后台私信我:前端,即可免费获取。

Virtual DOM

前端石器时代,我们页面交互和渲染,是通过服务端渲染或者直接操作DOM实现的, 有点像C/C++这类系统编程语言手动操纵内存. 那时候JQuery很火:

5a377df9ef5e2b257e5ac045dfdd6208.png

后来随着软硬件性能越来越好、Web应用也越来越复杂,前端开发者的生产力也要跟上,类似JQuery这种命令式的编程方式无疑是比较低效的. 尽管手动操作 DOM 可能可以达到更高的性能和灵活性,但是这样对大部分开发者来说太低效了,我们是可以接受牺牲一点性能换取更高的开发效率的.

怎么解决,再加一层吧,后来React就搞了一层VirtualDOM。我们可以声明式、组合式地构建一颗对象树, 然后交由React将它映射到DOM:

3094482d58a8a7332324bb0a4bc43eab.png

一开始VirtualDOM和DOM的关系比较暧昧,两者是耦合在一起的。后面有人想,我们有了VirtualDOM这个抽象层,那应该能多搞点别的,比如渲染到移动端原生组件、PDF、Canvas、终端UI等等。

后来VirtualDOM进行了更彻底的分层,有着这个抽象层我们可以将VirtualDOM映射到更多类似应用场景:

c530f6d2cadd0d3663fdd1cdc19d1a31.png

所以说 VirtualDOM 更大的意义在于开发方式的转变: 声明式、 数据驱动, 让开发者不需要关心 DOM 的操作细节(属性操作、事件绑定、DOM 节点变更),换句话说应用的开发方式变成了view=f(state), 这对生产力的解放是有很大推动作用的; 另外有了VirtualDOM这一层抽象层,使得多平台渲染成为可能。

当然VirtualDOM或者React,不是唯一,也不是第一个这样的解决方案。其他前端框架,例如Vue、Angular基本都是这样一个发展历程。

上面说了,分层不是银弹。我们通过ReactNative可以开发跨平台的移动应用,但是众所周知,它运行效率或者灵活性暂时是无法与原生应用比拟的。

Taro

Taro 和React一样也采用分层架构风格,只不过他们解决的问题是相反的。React加上一个分层,可以渲染到不同的视图形态;而Taro则是为了统一多样的视图形态: 国内现如今市面上端的形态多种多样,Web、React-Native、微信小程序...... 针对不同的端去编写多套代码的成本非常高,这种需求催生了Taro这类框架的诞生. 使用 Taro,我们可以只书写一套代码, 通过编译工具可以输出到不同的端:

5e10f54340e6345993f3f54651297982.png

(图片来源: 多端统一开发框架 - Taro)

管道和过滤器

在管道/过滤器架构风格中,每个组件都有一组输入和输出,每个组件职责都很单一, 数据输入组件,经过内部处理,然后将处理过的数据输出。所以这些组件也称为过滤器,连接器按照业务需求将组件连接起来,其形状就像‘管道’一样,这种架构风格由此得名。

19a158f1ba2fbc2c6d5b03330fc9c464.png

这里面最经典的案例是*unix Shell命令,Unix的哲学就是“只做一件事,把它做好”,所以我们常用的Unix命令功能都非常单一,但是Unix Shell还有一件法宝就是管道,通过管道我们可以将命令通过标准输入输出串联起来实现复杂的功能:

# 获取网页,并进行拼写检查。代码来源于wikicurl "http://en.wikipedia.org/wiki/Pipeline_(Unix)" | sed 's/[^a-zA-Z ]/ /g' | r 'A-Z ' 'a-z' | grep '[a-z]' | sort -u | comm -23 - /usr/share/dict/words | less

另一个和Unix管道相似的例子是ReactiveX, 例如RxJS. 很多教程将Rx比喻成河流,这个河流的开头就是一个事件源,这个事件源按照一定的频率发布事件。Rx真正强大的其实是它的操作符,有了这些操作符,你可以对这条河流做一切可以做的事情,例如分流、节流、建大坝、转换、统计、合并、产生河流的河流......

这些操作符和Unix的命令一样,职责都很单一,只干好一件事情。但我们管道将它们组合起来的时候,就迸发了无限的能力.

import { fromEvent } from 'rxjs';import { throttleTime, map, scan } from 'rxjs/operators';fromEvent(document, 'click') .pipe( throttleTime(1000), map(event => event.clientX), scan((count, clientX) => count + clientX, 0) ) .subscribe(count => console.log(count));

除了上述的RxJS,管道模式在前端领域也有很多应用,主要集中在前端工程化领域。例如'老牌'的项目构建工具Gulp, Gulp使用管道化模式来处理各种文件类型,管道中的每一个步骤称为Transpiler(转译器), 它们以 NodeJS 的Stream 作为输入输出。整个过程高效而简单。

bb7fbe5611e1c41c8c319a1d6aa6ce29.png

不确定是否受到Gulp的影响,现代的Webpack打包工具,也使用同样的模式来实现对文件的处理, 即Loader, Loader 用于对模块的源代码进行转换, 通过Loader的组合,可以实现复杂的文件转译需求.

// webpack.config.jsmodule.exports = { ... module: { rules: [{ test: /.scss$/, use: [{ loader: "style-loader" // 将 JS 字符串生成为 style 节点 }, { loader: "css-loader" // 将 CSS 转化成 CommonJS 模块 }, { loader: "sass-loader" // 将 Sass 编译成 CSS }] }] }};

中间件(Middleware)

d80cd5ae60f320aa273392ea073316fc.png

如果开发过Express、Koa或者Redux, 你可能会发现中间件模式和上述的管道模式有一定的相似性,如上图。相比管道,中间件模式可以使用一个洋葱剖面来形容。但和管道相比,一般的中间件实现有以下特点:

  • 中间件没有显式的输入输出。这些中间件之间通常通过集中式的上下文对象来共享状态
  • 有一个循环的过程。管道中,数据处理完毕后交给下游了,后面就不管了。而中间件还有一个回归的过程,当下游处理完毕后会进行回溯,所以有机会干预下游的处理结果。

我在谷歌上搜了老半天中间件,对于中间件都没有得到一个令我满意的定义. 暂且把它当作一个特殊形式的管道模式吧。这种模式通常用于后端,它可以干净地分离出请求的不同阶段,也就是分离关注点。比如我们可以创建这些中间件:

  • 日志:记录开始时间 ⏸ 计算响应时间,输出请求日志
  • 认证:验证用户是否登录
  • 授权:验证用户是否有执行该操作的权限
  • 缓存:是否有缓存结果,有的话就直接返回 ⏸ 当下游响应完成后,再判断一下响应是否可以被缓存
  • 执行:执行实际的请求处理 ⏸ 响应

有了中间件之后,我们不需要在每个响应处理方法中都包含这些逻辑,关注好自己该做的事情。下面是Koa的示例代码:

const Koa = require('koa');const app = new Koa();// loggerapp.use(async (ctx, next) => { await next(); const rt = ctx.response.get('X-Response-Time'); console.log(`${ctx.method} ${ctx.url} - ${rt}`);});// x-response-timeapp.use(async (ctx, next) => { const start = Date.now(); await next(); const ms = Date.now() - start; ctx.set('X-Response-Time', `${ms}ms`);});// responseapp.use(async ctx => { ctx.body = 'Hello World';});app.listen(3000);

事件驱动

事件驱动, 或者称为发布-订阅风格, 对于前端开发来说是再熟悉不过的概念了. 它定义了一种一对多的依赖关系, 在事件驱动系统风格中,组件不直接调用另一个组件,而是触发或广播一个或多个事件。系统中的其他组件在一个或多个事件中注册。当一个事件被触发,系统会自动通知在这个事件中注册的所有组件.

这样就分离了关注点,订阅者依赖于事件而不是依赖于发布者,发布者也不需要关心订阅者,两者解除了耦合

生活中也有很多发布-订阅的例子,比如微信公众号信息订阅,当新增一个订阅者的时候,发布者并不需要作出任何调整,同样发布者调整的时候也不会影响到订阅者,只要协议没有变化。我们可以发现,发布者和订阅者之间其实是一种弱化的动态的关联关系

解除耦合目的是一方面, 另一方面也可能由基因决定的,一些事情天然就不适合或不支持用同步的方式去调用,或者这些行为是异步触发的

JavaScript的基因决定事件驱动模式在前端领域的广泛使用. 在浏览器和Node中的JavaScript是如何工作的? 可视化解释 简单介绍了Javascript的执行原理,其中提到JavaScript是单线程的编程语言,为了应对各种实际的应用场景,一个线程以压根忙不过来的,事件驱动的异步方式是JavaScript的救命稻草.

浏览器方面,浏览器就是一个GUI程序,GUI程序是一个循环(更专业的名字是事件循环),接收用户输入,程序处理然后反馈到页面,再接收用户输入... 用户的输入是异步,将用户输入抽象为事件是最简洁、自然、灵活的方式。

需要注意的是:事件驱动和异步是不能划等号的。异步 !== 事件驱动,事件驱动 !== 异步

扩展:

  • 响应式编程: 响应式编程本质上也是事件驱动的,下面是前端领域比较流行的两种响应式模式:
  • 函数响应式(Functional Reactive Programming), 典型代表RxJS
  • 透明的函数响应式编程(Transparently applying Functional Reactive Programming - TFRP), 典型代表Vue、Mobx
  • 消息总线:指接收、发送消息的软件系统。消息基于一组已知的格式,以便系统无需知道实际接收者就能互相通信

MV*

MV*架构风格应用也非常广泛。我觉MV*本质上也是一种分层架构,一样强调职责分离。其中最为经典的是MVC架构风格,除此之外还有各种衍生风格,例如MVP、MVVM、MVI(Model View Intent). 还有有点关联Flux或者Redux模式。

家喻户晓的MVC

9591d1689172cff12d1a34baa5e289ad.png

如其名,MVC将应用分为三层,分别是:

  • 视图层(View) 呈现数据给用户
  • 控制器(Controller) 模型和视图之间的纽带,起到不同层的组织作用:
  • 处理事件并作出响应。一般事件有用户的行为(比如用户点击、客户端请求),模型层的变更
  • 控制程序的流程。根据请求选择适当的模型进行处理,然后选择适当的视图进行渲染,最后呈现给用户
  • 模型(Model) 封装与应用程序的业务逻辑相关的数据以及对数据的处理方法, 通常它需要和数据持久化层进行通信

目前前端应用很少有纯粹使用MVC的,要么视图层混合了控制器层,要么就是模型和控制器混合,或者干脆就没有所谓的控制器. 但一点可以确定的是,很多应用都不约而同分离了'逻辑层'和'视图层'。

下面是典型的AngularJS代码, 视图层:

Todo

{{todoList.remaining()}} of {{todoList.todos.length}} remaining [ archive ] {{todo.text}}

逻辑层:

angular.module('todoApp', []) .controller('TodoListController', function() { var todoList = this; todoList.todos = [ {text:'learn AngularJS', done:true}, {text:'build an AngularJS app', done:false}]; todoList.addTodo = function() { todoList.todos.push({text:todoList.todoText, done:false}); todoList.todoText = ''; }; todoList.remaining = function() { var count = 0; angular.forEach(todoList.todos, function(todo) { count += todo.done ? 0 : 1; }); return count; }; todoList.archive = function() { var oldTodos = todoList.todos; todoList.todos = []; angular.forEach(oldTodos, function(todo) { if (!todo.done) todoList.todos.push(todo); }); }; });

至于MVP、MVVM,这些MVC模式的延展或者升级,网上都大量的资源,这里就不予赘述。

Redux

Redux是Flux架构的改进、融合了Elm语言中函数式的思想. 下面是Redux的架构图:

b6ddb0f7aeb0da1a45964e68d52fbb89.png

从上图可以看出Redux架构有以下要点:

  • 单一的数据源.
  • 单向的数据流.

单一数据源, 首先解决的是传统MVC架构多模型数据流混乱问题(如下图)。单一的数据源可以让应用的状态可预测和可被调试。另外单一数据源也方便做数据镜像,实现撤销/重做,数据持久化等等功能

4a1a9af678b43e09d6f353a88aa06a47.png

单向数据流用于辅助单一数据源, 主要目的是阻止应用代码直接修改数据源,这样一方面简化数据流,同样也让应用状态变化变得可预测。

上面两个特点是Redux架构风格的核心,至于Redux还强调不可变数据、利用中间件封装副作用、范式化状态树,只是一种最佳实践。还有许多类Redux的框架,例如Vuex、ngrx,在架构思想层次是一致的:

ed0da4a9a554d3c0054af5a3b57ab5de.png

复制风格

265ba0b9b0e42175f447f52d405530ba.png

基于复制(Replication)风格的系统,会利用多个实例提供相同的服务,来改善服务的可访问性和可伸缩性,以及性能。这种架构风格可以改善用户可察觉的性能,简单服务响应的延迟。

这种风格在后端用得比较多,举前端比较熟悉的例子,NodeJS. NodeJS是单线程的,为了利用多核资源,NodeJS标准库提供了一个cluster模块,它可以根据CPU数创建多个Worker进程,这些Worker进程可以共享一个服务器端口,对外提供同质的服务, Master进程会根据一定的策略将资源分配给Worker:

const cluster = require('cluster');const http = require('http');const numCPUs = require('os').cpus().length;if (cluster.isMaster) { console.log(`Master ${process.pid} is running`); // Fork workers. for (let i = 0; i < numCPUs; i++) { cluster.fork(); } cluster.on('exit', (worker, code, signal) => { console.log(`worker ${worker.process.pid} died`); });} else { // Workers可以共享任意的TCP连接 // 比如共享HTTP服务器 http.createServer((req, res) => { res.writeHead(200); res.end('hello world'); }).listen(8000); console.log(`Worker ${process.pid} started`);}

利用多核能力可以提升应用的性能和可靠性。我们也可以利用PM2这样的进程管理工具,来简化Node集群的管理,它支持很多有用的特性,例如集群节点重启、日志归集、性能监视等。

复制风格常用于网络服务器。浏览器和Node都有Worker的概念,但是一般都只推荐在CPU密集型的场景使用它们,因为浏览器或者NodeJS内置的异步操作已经非常高效。实际上前端应用CPU密集型场景并不多,或者目前阶段不是特别实用。除此之外你还要权衡进程间通信的效率、Worker管理复杂度、异常处理等事情。

有一个典型的CPU密集型的场景,即源文件转译. 典型的例子是CodeSandbox, 它就是利用浏览器的Worker机制来提高源文件的转译性能的:

5d3c613c2612b32ad2d351bf03d94d37.png

除了处理CPU密集型任务,对于浏览器来说,Worker也是一个重要的安全机制,用于隔离不安全代码的执行,或者限制访问浏览器DOM相关的东西。小程序抽离逻辑进程的原因之一就是安全性

其他示例:

  • ServerLess

微内核架构

c77e2b3a23536e7bbfd470932a80aada.png

微内核架构(MicroKernel)又称为"插件架构

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

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

相关文章

十九、PHP框架Laravel学习笔记——批量赋值和软删除

一&#xff0e;批量赋值 上一节增删改中&#xff0c;新增中我们发现需要进行批量赋值的许可&#xff1b;一般情况下&#xff0c;是为了防止提交过来的字段在部分场景中不需要或不能&#xff1b;所以&#xff0c;我们需要通过黑白名单机制进行过滤掉必要的字段&#xff1b; //通…

codevs-2235

2235 机票打折 题目描述 Description.输入机票原价&#xff08;3到4位的正整数&#xff0c;单位&#xff1a;元&#xff09;&#xff0c;再输入机票打折率&#xff08;小数点后最多一位数字&#xff09;。编程计算打折后机票的实际价格&#xff08;单位&#xff1a;元。计算结果…

speech production model

文章目录1 概述2 source model3 filter model4 小结参考资料1 概述 本文的目的是为了厘清在speech production model中source model和filter model所扮演的角色&#xff0c;不涉及具体公式的推导或者模型的建立&#xff0c;只是为了把这两个model在干什么事情说明白。文中用到…

python装饰器函数执行后日志_python 装饰器理解

在理解装饰器之前&#xff0c;先应该对闭包有个概念&#xff1a;所谓闭包&#xff0c;就是将组成函数的语句和这些语句的执行环境打包在一起时得到的对象&#xff0c;它的主要作用是封存上下文。这一特性可以巧妙的被用于现有函数的包装&#xff0c;从而为现有函数添加功能&…

二十、PHP框架Laravel学习笔记——模型的作用域

一&#xff0e;本地作用域 很多情况下&#xff0c;我们在数据查找时有一部分条件会被重复且大量使用&#xff1b;而这个条件&#xff0c;可能只是在这个模型对应的数据表使用&#xff0c;别的表并不使用&#xff1b;那么这种情况&#xff0c;可以使用本地作用域的方式&#xf…

App接口如何保证安全

微信开发或者高德地图&#xff0c;百度地图什么的api要使用&#xff0c;使用之前都需要注册一个账号&#xff0c;然后系统会给你一个key&#xff0c;然后调用api的时候把key传给服务器。 平常公司内部开发项目时&#xff0c;直接用mvc为app客户端提供接口&#xff0c;没有做过验…

论文阅读 - Joint Beat and Downbeat Tracking with Recurrent Neural Networks

文章目录1 概述2 信号预处理3 分类神经网络4 动态贝叶斯网络&#xff08;HMM&#xff09;4.1 原始的bar pointer model4.2 原始的bar pointer model的缺点4.3 改进后的模型5 预测参考资料1 概述 最近在做音乐卡点相关的项目&#xff0c;需要对音乐的基本特征进行理解&#xff…

二十一、PHP框架Laravel学习笔记——模型的访问器和修改器

一&#xff0e;访问器 访问器&#xff1a;就是在获取数据列表时&#xff0c;拦截属性并对属性进行修改的过程&#xff1b;比如&#xff0c;我们在输出性别时&#xff0c;在性别左右加上括号&#xff0c;或给邮件转换为大写&#xff1b; //访问器&#xff0c;前固定 get&#…

python将txt转json_Python控制乐高EV3,以及VSCODE环境配置

乐高EV3的可扩展性很强&#xff0c;但如何用pc连接ev3&#xff0c;并用python代码来控制EV3&#xff0c;资料太少了&#xff0c;试着做了一次&#xff0c;记录在这里。需要的硬/软件硬件准备一、乐高EV3二、PC&#xff0c;win10系统三、TF卡&#xff08;我用的是64G的&#xff…

移动文件读/写指针----lseek

头文件&#xff1a;#include<sys/types.h>、#include<unistd.h> 函数原型&#xff1a;off_t lseek(int fildes,off_t offset,int whence) 参数说明&#xff1a;fildes:文件描述符 offset:偏移量&#xff0c;正数表示正向偏移&#xff0c;负数表示负向偏移。 whence…

Yolo系列知识点梳理(Yolov1-v5)

文章目录1 概述2 Yolo系列模型2.1 基石 - Yolov12.1.1 Yolov1的网络结构2.1.2 Yolov1的feature map2.1.3 Yolov1的训练2.1.4 Yolov1的预测2.1.5 Yolov1小结2.2 Yolo9000 - Yolov22.2.1 Better2.2.1.1 引入了Batch normalization2.2.1.2 高分辨率的分类器2.2.1.3 加入了anchor机…

二十二、PHP框架Laravel学习笔记——集合的使用

一&#xff0e;创建集合 什么是集合&#xff1f;即&#xff1a;它是一种更具读取性和处理能力的数组封装&#xff1b;比如&#xff0c;我们从数据库得到的数据列表&#xff0c;它就是一种集合&#xff1b;数据集合&#xff0c;提供了大量的方法方便我们进行各种操作&#xff1…

神经网络用python还是matlab_Matlab与神经网络学习笔记

这是学习《Neural Network and Deep Learning》的笔记。在刚开始学习时&#xff0c;电脑安装的是python3&#xff0c;而作者用的是2.我想着反正要熟悉代码&#xff0c;大学期间MATLAB用的比较多&#xff0c;也懒得去折腾python2或是重写成python3.于是用MATLAB完成了里面的实例…

二十三、PHP框架Laravel学习笔记——集合的常用方法

一&#xff0e;常用方法 all()方法&#xff0c;转换为属性形式输出&#xff0c;使用 dd 方法看类型&#xff1b; $collection collect([1, 2, 2, 3, 4, 4, 4]); dd($collection->all()); PS&#xff1a;$collection->dd()方法可以以 dd()模式输出&#xff0c;还有 du…

mac看图软件哪个好用_细数Mac上那些好用且免费的软件(三)

许多朋友购买了Mac电脑之后发现很多软件都没有&#xff0c;而且苹果商店好多软件都是收费的。那有没有免费的软件也能满足日常的需求呢&#xff1f;macw小编就为大家推荐一些免费且超级好用的软件&#xff0c;赶快来看看有没有你需要的吧&#xff01;细数Mac上那些好用且免费的…

图像表格实线和虚线检测

文章目录1 背景简述2 camelot中的方法2.1 二值化2.2 腐蚀膨胀2.3 轮廓检测2.4 结果展示3 基于霍夫直线检测的方法3.1 霍夫直线检测原理3.2 概率霍夫直线检测3.3 霍夫直线应用参考资料1 背景简述 图像中的表格结构化是一个比较热门的话题&#xff0c;其输入是一张图片&#xff…

二十四、PHP框架Laravel学习笔记——模型的数据集合

一&#xff0e;数据集合 数据集合&#xff0c;就是已经将模型方法 get()获取到的数据再进行处理&#xff1b;比如&#xff1a;map()方法&#xff0c;通过它可以实现类似访问器一样对字段进行处理的效果&#xff1b; $users User::get(); //使用集合方法 map 可以对输出的字…

const参数,const返回值与const函数

在C程序中&#xff0c;经常用const 来限制对一个对象的操作&#xff0c;例如&#xff0c;将一个变量定义为const 的&#xff1a; const int n3; 则这个变量的值不能被修改&#xff0c;即不能对变量赋值。 const 这个关键字经常出现在函数的定义中&#xff0c;而且会出现在不同…

python简笔画怎么画_怎样用tk语句在Python下画一棵树

展开全部 1.代码的结2113构&#xff1a; 本代码有两个子5261函数组成&#xff0c;据图有main函数4102和画树函数组成。 2.编写画1653树函数&#xff1a; 画树函数&#xff0c;就是用来画出我们的树的一种子函数&#xff0c;代码如下&#xff1a;def tree(plist,l,a,f): if l>…

论文阅读 - AUTOVC: Zero-Shot Voice Style Transfer with Only Autoencoder Loss

文章目录1 概述2 模型架构3 模块解析3.1 获取梅尔频谱3.2 speaker encoder3.3 AutoVC3.4 Vocoder4 关键部分参考资料1 概述 voice conversion这个任务的目标是输入两个音频&#xff0c;其输入是两段音频&#xff0c;一段音频称为content_audio&#xff0c;另一段称为speaker_a…