Java设计模式-策略模式(行为型)
策略模式详解
一、策略模式概述
1.1 基本概念
策略模式是一种行为型设计模式,它主要用于处理算法的不同变体。其核心思想是将算法的定义与使用分离开来,把一系列具体的算法封装成独立的策略类,这些策略类实现相同的策略接口。客户端可以在运行时根据实际情况动态地选择并使用不同的策略,从而使得算法的变化不会影响到使用算法的客户端代码。
1.2 实际应用场景举例
在生活中,我们可以找到很多类似策略模式的例子。比如出行方式的选择,从一个地方到另一个地方,我们可以选择步行、骑自行车、乘坐公交车、打车等不同的出行方式。每一种出行方式就相当于一个具体的策略。如果把目的地看作是客户端,那么客户端可以根据自己的需求(如时间是否充裕、经济成本、个人喜好等) 来选择合适的出行策略。
在软件开发中,策略模式也有广泛的应用。例如在一个游戏中,角色的攻击方式有多种,像近战攻击、远程魔法攻击、投掷武器攻击等。我们可以将每种攻击方式封装成一个策略类,游戏中的角色类(客户端)可以根据不同的情况(如敌人的距离、自身的状态等)来选择合适的攻击策略。这样,当游戏需要添加新的攻击方式时,只需要新增一个策略类实现相应的攻击策略接口,而不需要修改角色类的核心代码。
1.3 解决的问题
在传统的设计中,如果一个类中包含多种算法的实现,当需要修改或扩展其中某个算法时,可能会涉及到对整个类的修改,这会增加代码的维护难度,并且容易引入新的错误。例如,有一个排序类,里面同时实现了冒泡排序、选择排序和快速排序等多种排序算法。如果后续需要优化快速排序算法,就需要直接在这个类中修改相关代码,这可能会影响到其他排序算法的正常运行。
策略模式通过将不同的算法封装到独立的策略类中,使得算法的修改和扩展更加容易。每个策略类只负责一种算法的实现,当需要修改某个算法时,只需要修改对应的策略类,而不会影响到其他策略类和使用这些策略的客户端代码。同时,当需要添加新的算法时,也只需要新增一个策略类实现策略接口即可,符合软件设计中的开闭原则(对扩展开放,对修改关闭)。
二、策略模式原理剖析
2.1 策略模式的结构
策略模式主要包含以下三种角色:
- 策略(Strategy):这是一个接口,它定义了若干个抽象方法,这些抽象方法就是算法的标识。通过这个接口,具体的策略类可以实现不同的算法逻辑。
- 具体策略(ConcreteStrategy):具体策略类实现了策略接口所定义的抽象方法,即给出了算法标识的具体实现。每个具体策略类对应一种具体的算法实现。
- 上下文(Context):上下文类依赖于策略接口,它包含一个策略接口声明的变量,用于保存具体策略的引用。上下文类提供一个方法,该方法委托策略变量调用具体策略所实现的策略接口中的方法。
2.2 策略模式的工作流程
首先,客户端创建具体策略类的实例,并将其传递给上下文类。上下文类在内部持有这个具体策略的引用。当客户端调用上下文类的方法时,上下文类通过其持有的策略引用调用具体策略类中实现的策略接口方法,从而执行相应的算法逻辑。整个过程中,客户端只需要关心选择合适的策略并传递给上下文类,而不需要了解具体策略内部的实现细节。
2.3 示例说明
以之前提到的游戏角色攻击方式为例,我们来详细说明策略模式的工作流程。
- 定义策略接口:
interface AttackStrategy {void attack(); // 定义攻击方法
}
这里的AttackStrategy
接口就是策略接口,它定义了攻击这个算法的标识,即attack
方法。
- 创建具体策略类:
- 近战攻击策略类:
class MeleeAttack implements AttackStrategy {@Overridepublic void attack() {System.out.println("角色进行近战攻击,挥舞武器冲向敌人!");}
}
- 远程魔法攻击策略类:
class MagicAttack implements AttackStrategy {@Overridepublic void attack() {System.out.println("角色进行远程魔法攻击,释放魔法球攻击敌人!");}
}
这两个类分别实现了AttackStrategy
接口,给出了不同的攻击算法实现,属于具体策略类。
- 定义上下文类:
class Character {private AttackStrategy attackStrategy; // 持有攻击策略接口的引用public void setAttackStrategy(AttackStrategy attackStrategy) {this.attackStrategy = attackStrategy;}public void performAttack() {attackStrategy.attack(); // 委托策略执行攻击}
}
Character
类就是上下文类,它通过setAttackStrategy
方法接收具体的攻击策略,并在performAttack
方法中委托持有的攻击策略执行攻击操作。
- 客户端使用:
public class Main {public static void main(String[] args) {Character character = new Character();// 使用近战攻击策略AttackStrategy melee = new MeleeAttack();character.setAttackStrategy(melee);character.performAttack();// 更换为远程魔法攻击策略AttackStrategy magic = new MagicAttack();character.setAttackStrategy(magic);character.performAttack();}
}
在客户端代码中,首先创建了一个Character
对象,然后分别创建了MeleeAttack
和MagicAttack
这两个具体策略的实例,并将它们依次传递给Character
对象,从而实现了不同攻击策略的切换和使用。
三、策略模式的实现
3.1 以士兵列队为例的代码实现
在士兵列队场景中,长官面临不同列队需求的问题,如按号码从小到大、从大到小或按奇偶数排列等。若在Army
类的lineUp
方法直接修改代码实现,会使代码复杂且易出错。
使用策略模式解决:定义LineUpStrategy
接口规范列队方法;StrategyA
、StrategyB
、StrategyC
分别实现从小到大、从大到小、按奇偶数排列的具体策略;Army
类作为上下文,持有策略接口引用,通过setStrategy
设置策略,lineUp
方法委托策略执行列队。这样,客户端按需传递不同策略实例,就能灵活切换列队方式,新增需求时也只需添加新策略类,无需改动原有核心代码。
类图
3.1.1 定义策略接口(LineUpStrategy.java)
public interface LineUpStrategy {public abstract void arrange(int a[]);
}
这个接口定义了士兵列队的抽象方法arrange
,具体的列队策略类将实现这个方法。
3.1.2 具体策略类
- StrategyA.java(选择法,从小到大排序):
public class StrategyA implements LineUpStrategy {public void arrange(int a[]) {for (int i = 0; i < a.length; i++) {int index = i, j = 0;for (j = i + 1; j < a.length; j++) {if (a[j] < a[index]) {index = j;}}if (index != i) {int temp = a[i];a[i] = a[index];a[index] = temp;}}}
}
StrategyA
类使用选择排序算法,将数组a
中的元素从小到大进行排序,实现了LineUpStrategy
接口中的arrange
方法。
- StrategyB.java(冒泡法,从大到小排序):
public class StrategyB implements LineUpStrategy {public void arrange(int a[]) {int N = a.length;for (int m = 0; m < N - 1; m++) {for (int i = 0; i < N - 1 - m; i++) {if (a[i] < a[i + 1]) {int t = a[i + 1];a[i + 1] = a[i];a[i] = t;}}}}
}
StrategyB
类采用冒泡排序算法,把数组a
的元素从大到小进行排序,同样实现了LineUpStrategy
接口。
- StrategyC.java(按奇、偶分别排序):
public class StrategyC implements LineUpStrategy {public void arrange(int a[]) {int oddNumberAmount = 0;for (int i = 0; i < a.length; i++) {if (a[i] % 2 != 0)oddNumberAmount++;}int oddArray[] = new int[oddNumberAmount];int evenArray[] = new int[a.length - oddNumberAmount];for (int i = 0, m = 0, n = 0; i < a.length; i++) {if (a[i] % 2 != 0) {oddArray[m] = a[i];m++;} else {evenArray[n] = a[i];n++;}}for (int i = 0; i < evenArray.length; i++) {int index = i, j;for (j = i + 1; j < evenArray.length; j++) {if (evenArray[j] < evenArray[index])index = j;}if (index != i) {int temp = evenArray[i];evenArray[i] = evenArray[index];evenArray[index] = temp;}}int N = oddArray.length;for (int m = 0; m < N - 1; m++) {for (int i = 0; i < N - 1 - m; i++) {if (oddArray[i] < oddArray[i + 1]) {int t = oddArray[i + 1];oddArray[i + 1] = oddArray[i];oddArray[i] = t;}}}for (int i = 0; i < oddArray.length; i++) {a[i] = oddArray[i];}for (int i = 0; i < evenArray.length; i++) {a[i + oddArray.length] = evenArray[i];}}
}
StrategyC
类先将数组a
中的元素按奇、偶分开,然后分别对奇数和偶数进行排序,最后再合并起来,实现了独特的列队策略。
3.1.3 上下文类(Army.java)
public class Army {LineUpStrategy strategy;public void setStrategy(LineUpStrategy strategy) {this.strategy = strategy;}public void lineUp(int a[]) {if (strategy != null)strategy.arrange(a);elseSystem.out.println("没有列队策略");}
}
Army
类作为上下文类,持有LineUpStrategy
接口的引用,通过setStrategy
方法可以设置具体的列队策略,在lineUp
方法中委托持有的策略对士兵号码数组进行列队操作。
3.1.4 应用示例(Application.java)
import java.util.Arrays;
public class Application {public static void main(String args[]) {int soldierNumberOne[] = {3, 1, 6, 2, 4, 5};int soldierNumberTwo[] = {2, 5, 6, 3, 4, 1};int soldierNumberThree[] = {1, 3, 6, 2, 5, 4};Army 三连长 = new Army();// 使用StrategyA策略(从小到大排序)三连长.setStrategy(new StrategyA());三连长.lineUp(soldierNumberOne);System.out.println("列队情况(从小到大):");System.out.println(Arrays.toString(soldierNumberOne));// 使用StrategyB策略(从大到小排序)三连长.setStrategy(new StrategyB());三连长.lineUp(soldierNumberTwo);System.out.println("列队情况(从大到小):");System.out.println(Arrays.toString(soldierNumberTwo));// 使用StrategyC策略(奇、偶排列)三连长.setStrategy(new StrategyC());三连长.lineUp(soldierNumberThree);System.out.println("列队情况(奇、偶排列):");System.out.println(Arrays.toString(soldierNumberThree));}
}
在Application
类中,创建了Army
对象(三连长),并分别创建了不同的具体策略实例,将它们依次传递给Army
对象,从而实现了不同的列队策略,展示了策略模式在运行时动态切换算法的能力。
3.2 策略模式的类图(用 PlantUML 表示)
interface LineUpStrategy {+arrange(int[] a) : void
}class StrategyA {+arrange(int[] a) : void
}class StrategyB {+arrange(int[] a) : void
}class StrategyC {+arrange(int[] a) : void
}class Army {-strategy : LineUpStrategy+setStrategy(LineUpStrategy strategy) : void+lineUp(int[] a) : void
}LineUpStrategy <|.. StrategyA
LineUpStrategy <|.. StrategyB
LineUpStrategy <|.. StrategyC
Army --> LineUpStrategy
上述类图清晰地展示了策略模式中各个角色之间的关系。LineUpStrategy
接口是策略的抽象定义,StrategyA
、StrategyB
和StrategyC
是具体的策略实现类,它们都实现了LineUpStrategy
接口。Army
类作为上下文类,依赖于LineUpStrategy
接口,通过持有策略接口的引用,实现对具体策略的调用。
四、策略模式的优势
4.1 可维护性增强
由于每个具体策略类只负责一种算法的实现,当需要修改某个算法时,只需要修改对应的策略类即可,不会影响到其他策略类和使用这些策略的客户端代码。例如,在士兵列队的例子中,如果需要优化冒泡排序(StrategyB
类中的算法),只需要在StrategyB
类中进行修改,而StrategyA
、StrategyC
类以及Army
类等都不会受到影响。这样大大降低了代码修改的风险,使得代码的维护更加容易。
4.2 可扩展性提升
当需要添加新的算法时,只需要新增一个具体策略类实现策略接口即可,不需要对现有代码进行大规模的修改。还是以士兵列队为例,如果后续需要增加一种按年龄排序的列队策略,只需要创建一个新的类,实现LineUpStrategy
接口,编写按年龄排序的算法逻辑,然后在客户端代码中使用这个新的策略类即可,整个过程对原有代码的侵入性很小,方便了系统的扩展。
4.3 代码复用性提高
具体策略类可以在不同的上下文场景中被复用。比如在一个更复杂的军事模拟系统中,可能有多个部队(多个上下文类)都需要进行士兵列队操作,那么StrategyA
、StrategyB
、StrategyC
等具体策略类就可以被这些不同的部队类复用,避免了重复编写相同的算法代码,提高了代码的复用性。
4.4 符合开闭原则
策略模式通过将算法的变化封装在具体策略类中,使得系统对扩展开放(可以轻松添加新的策略类),对修改关闭(不需要修改使用策略的上下文类和其他已有的策略类)。这符合软件设计的开闭原则,保证了系统在面对需求变化时的稳定性和可维护性。
五、策略模式的应用场景
5.1 游戏开发领域
在游戏开发中,策略模式有很多应用场景。除了前面提到的角色攻击方式选择外,还有角色的移动方式(如步行、奔跑、飞行等)、技能释放策略(如单体攻击技能、群体攻击技能、辅助技能等)、游戏难度调整策略(简单、普通、困难模式对应的不同怪物属性、关卡难度等)等都可以使用策略模式来实现。通过策略模式,可以方便地在游戏运行时根据玩家的操作、游戏进度等因素动态切换不同的策略,增加游戏的趣味性和灵活性。
5.2 电商系统领域
在电商系统中,策略模式可用于多种场景。比如商品的促销策略,有满减、打折、买一送一、积分兑换等不同的促销方式,每种促销方式可以封装成一个具体策略类。在计算订单价格时,根据商品参与的促销活动选择相应的促销策略来计算最终价格。还有物流配送策略,不同的商品可能有不同的配送方式(普通快递、加急快递、自提等),可以通过策略模式来管理和选择合适的配送策略。
5.3 图形绘制与处理领域
在图形绘制和处理软件中,策略模式也能发挥作用。例如,绘制图形的填充方式有多种,如实心填充、渐变填充、图案填充等,每种填充方式可以作为一个具体策略类实现。绘图工具类(上下文类)可以根据用户的选择调用相应的填充策略来绘制图形。另外,图形的变换操作(如平移、旋转、缩放等)也可以使用策略模式来实现,方便用户在不同的变换需求之间进行切换。
5.4 数据处理与算法选择领域
在数据处理过程中,经常需要对数据进行不同方式的排序、查找、过滤等操作。例如,排序算法有冒泡排序、选择排序、快速排序、归并排序等,我们可以将每种排序算法封装成一个具体策略类。当需要对数据进行排序时,根据数据的规模、特点等因素选择合适的排序策略。同样,在查找算法(顺序查找、二分查找等)和过滤算法(按条件筛选数据)方面,也可以利用策略模式实现。
相关文章:

Java设计模式-策略模式(行为型)
策略模式详解 一、策略模式概述 1.1 基本概念 策略模式是一种行为型设计模式,它主要用于处理算法的不同变体。其核心思想是将算法的定义与使用分离开来,把一系列具体的算法封装成独立的策略类,这些策略类实现相同的策略接口。客户端可以在…...

html body 设置heigth 100%,body内元素设置margin-top出滚动条(margin 重叠问题)
今天在用移动端的时候发现个问题,html,body 设置 height:100% 会出现纵向滚动条 <!DOCTYPE html> <html> <head> <title>html5</title> <style> html, body {height: 100%; } * {margin: 0;padding: 0; } </sty…...

C语言模糊不清的知识
1、malloc、calloc、realloc的区别和用法 malloc实在堆上申请一段连续指定大小的内存区域,并以void*进行返回,不会初始化内存。calloc与malloc作用一致,只是calloc会初始化内存,自动将内存清零。realloc用于重新分配之前通过mallo…...

如何配置光猫+路由器实现外网IP访问内部网络?
文章目录 前言一、网络拓扑理解二、准备工作三、光猫配置3.1 光猫工作模式3.2 光猫端口转发配置(路由模式时) 四、路由器配置4.1 路由器WAN口配置4.2 端口转发配置4.3 动态DNS配置(可选) 五、防火墙设置六、测试配置七、安全注意事…...

springboot3+vue3融合项目实战-大事件文章管理系统获取用户详细信息-ThreadLocal优化
一句话本质 为每个线程创建独立的变量副本,实现多线程环境下数据的安全隔离(线程操作自己的副本,互不影响)。 关键解读: 核心机制 • 同一个 ThreadLocal 对象(如示意图中的红色区域 tl)被多个线…...

【高数上册笔记篇02】:数列与函数极限
【参考资料】 同济大学《高等数学》教材樊顺厚老师B站《高等数学精讲》系列课程 (注:本笔记为个人数学复习资料,旨在通过系统化整理替代厚重教材,便于随时查阅与巩固知识要点) 仅用于个人数学复习,因为课…...

c++STL-string的模拟实现
cSTL-string的模拟实现 string的模拟实现string的模拟线性表的实现构造函数析构函数获取长度(size)和获取容量(capacity)访问 [] 和c_str迭代器(iterator)交换swap拷贝构造函数赋值重载(&#x…...

YashanDB(崖山数据库)V23.4 LTS 正式发布
2024年回顾 2024年11月我们受邀去深圳参与了2024国产数据库创新生态大会。在大会上崖山官方发布了23.3。这个也是和Oracle一样采用的事编年体命名。 那次大会官方希望我们这些在一直从事在一线的KOL帮助产品提一些改进建议。对于这样的想法,我们都是非常乐于合作…...

python 写一个工作 简单 番茄钟
1、图 2、需求 番茄钟(Pomodoro Technique)是一种时间管理方法,由弗朗西斯科西里洛(Francesco Cirillo)在 20 世纪 80 年代创立。“Pomodoro”在意大利语中意为“番茄”,这个名字来源于西里洛最初使用的一个…...
C++.IP协议通信
C++IP协议通信 1. TCP协议通信1.1 服务端实现创建套接字绑定地址监听连接接受连接数据传输关闭连接1.2 客户端实现创建套接字连接服务器数据传输关闭连接1.3 示例代码服务端代码示例客户端代码示例绑定地址接收数据发送数据关闭套接字2.2 客户端实现创建套接字发送数据接收数据…...
css背景相关
背景书写 background: url(src); // 注意:在写动态样式时,backgournd赋值格式错误,是不会在浏览器dom的style上显示的 // 但是可以创建不可见的img,预加载来提高性能背景也会加载图片资源 同img的src一样,background也…...

PyCharm 加载不了 conda 虚拟环境,不存在的
#工作记录 前言 在开发过程中,PyCharm 无法加载 Conda 虚拟环境是常见问题。 在不同情况下,“Conda 可执行文件路径”的指定可能会发生变化,不会一尘不变,需要灵活处置。 以下是一系列解决此问题的经验参考。 检查 Conda 安装…...

设计模式学习整理
目录 UML类图 设计模式六大原则 1.单一职责原则 2.里氏替换原则 3.依赖倒置原则 4.接口隔离原则 5.迪米特法则(最少知道原则) 6.开(放封)闭原则 设计模式分类 1.创建型模式 2.结构型模式 4.行为型模式 一、工厂模式(factory——简单工厂模式和抽象工厂模式) 1.1、…...

二分查找的理解
#define _CRT_SECURE_NO_WARNINGS #include <stdio.h>int binary_search(int arr[], int k, int sz) {int left 0;int right sz - 1;//这个是下标,减一是因为在0开始的,怕越界(访问无效)while (left < right){int mid…...

【Java】线程实例化 线程状态 线程属性
线程实例化 继承 Thread 类 创建类继承自 Thread 类 . class MyThread extends Thread重写 run() 方法 . Overridepublic void run(){// 线程要执行的任务代码}实例化自定义线程类 . 实现 Runnable 接口 创建类实现 Runnable 接口 . class MyRunnable implements Runnable实…...

卫宁健康WiNGPT3.0与WiNEX Copilot 2.2:医疗AI创新的双轮驱动分析
引言:医疗AI的双翼时代 在医疗信息化的浪潮中,人工智能技术的深度融入正在重塑整个医疗行业。卫宁健康作为国内医疗健康和卫生领域数字化解决方案的领军企业,持续探索AI技术在医疗场景中的创新应用。2025年5月10日,在第29届中国医院信息网络大会(CHIMA2025)上,卫宁健康…...

I2C通讯
3.1. 本章节的代码仓库 1 2 3 4 5 6 #如之前有获取则可跳过 #获取仓库 git clone https://gitee.com/LubanCat/lubancat_rk_code_storage.git#代码所在的位置 lubancat_rk_code_storage/quick_start/i2c3.2. i2c I2C(Inter-Integrated Circuit)是一种通用的总线协…...

Excel实现单元格内容拼接
一、应用场景: 场景A:将多个单元格拼接,比如写测试用例时,将多个模块拼接,中间用“-”隔开 场景B:将某单元格内容插入另一单元格固定位置(例如在B1中添加A1的内容) 二、实际应用&a…...

2025前端面试遇到的问题(vue+uniapp+js+css)
Vue相关面试题 vue2和vue3的区别 一、核心架构差异 特性Vue2Vue3响应式系统基于Object.defineProperty基于Proxy(支持动态新增/删除属性)代码组织方式Options API(data/methods分块)Composition API(逻辑按功能聚合&am…...
大模型核心基础简介
大模型核心基础简介目录 一、大模型简介定义核心特征 二、大模型的发展历程1. 早期奠基(1950s–2010s)1950s–1980s:神经网络萌芽1990s–2010s:深度学习前夜 2. 架构革命:Transformer的诞生与预训练范式(20…...

广东省省考备考(第八天5.11)—言语:逻辑填空(每日一练)
错题 解析 第一空,搭配“期盼”,且根据“生命,是来自上天的馈赠”,可知父母对孩子的出生是非常期盼的。A项“望穿秋水”,形容对远地亲友的殷切盼望,C项“望眼欲穿”,形容盼望殷切,均…...

github+ Picgo+typora
github Picgotypora 本文将介绍如何使用Picgo在typora中实现上传服务 创建github仓库以及配置token 创建仓库 注意需要Initialize 添加README 配置为public 配置token github点击头像找到setting 选择Developer setting 配置token generate 选第一个第二个都行(我这里选第…...

[网安工具] IP 信息收集工具 —— LBD · 使用手册
🌟想了解其它网安工具?看看这个:[网安工具] 网络安全工具管理 —— 工具仓库 管理手册 lbd | Kali Linux ToolsVideolbd Usage ExampleTest to see if the target domain (example.com) is using a load balancer:rootkali:~# lbd example.c…...
本贴会成为记录贴
这几天有些心力交瘁了 一方面带着对互联网下行的伤心,一方面是对未来的担忧 一转眼好像就是20 21那个 可以在宿舍肆意玩手机 大学生活 可是我不小了 是个26岁的人了 时间很快 快的就好像和自己开了一个玩笑 我以为可以找到一个自己足够喜欢的 可爱的人 可是我没有 …...

说说es配置项的动态静态之分和集群配置更新API
这天因为某件工作来到了es官网某个参数配置相关的页面,注意到了下图圆圈里的“Dynamic”: 链接:https://www.elastic.co/guide/en/elasticsearch/reference/8.1/modules-cluster.html#misc-cluster-settings 显然这是对配置项的一个描述&am…...

LLMs之Mistral Medium 3:Mistral Medium 3的简介、安装和使用方法、案例应用之详细攻略
LLMs之Mistral Medium 3:Mistral Medium 3的简介、安装和使用方法、案例应用之详细攻略 目录 Mistral Medium 3 简介 1、Mistral Medium 3 特点 Mistral Medium 3 安装和使用方法 2、使用方法 (1)、创建Agent (2)、模型微调 Mistral Medium 3 案例应用 Mistr…...

并发设计模式实战系列(17):信号量(Semaphore)
🌟 大家好,我是摘星! 🌟 今天为大家带来的是并发设计模式实战系列,第十七章信号量(Semaphore),废话不多说直接开始~ 目录 一、核心原理深度拆解 1. 信号量本质模型 2. 并发控制…...

RAGMCP基本原理说明和相关问题解惑
一、RAG架构原理和局限性 1.1 概念解释 RAG(Retrieval-Augmented Generation):检索增强生成,让大模型接受外部输入后,总结输出 向量数据库:向量数据通常是高维空间中的点,代表复杂的数据结构…...

Java学习手册:服务注册与发现
一、服务注册与发现的概念 在微服务架构中,服务注册与发现是核心功能之一。由于微服务架构中服务实例的数量和位置是动态变化的,服务注册与发现机制允许服务实例在启动时自动注册到注册中心,并在停止时自动注销。其他服务可以通过查询注册中…...
双向Transformer:BERT(Bidirectional Encoder Representations from Transformers)
基于Transformer架构,通过双向上下文建模训练,提高完成任务的性能。 一 BERT的核心理念 1.1双向上下文建模依赖 之前讲的双向递归是用两个RNN进行,而BERT是通过Transformer的自注意力机制同时捕捉上下文信息。 1.1.1掩码语言模型…...