策略模式优雅实践

1 什么是策略模式

        策略模式(Strategy Pattern)是一种常用的面向对象设计模式,它定义了一系列可互相替换的算法或策略,并将每个算法封装成独立的对象,使得它们可以在运行时动态地替换。具体来说,策略模式定义了一系列算法,每个算法都封装在一个具体的策略类中,这些策略类实现了相同的接口或抽象类。在使用算法的时候,客户端通过一个上下文对象来调用策略类的方法,从而完成算法的执行。这样,客户端可以在运行时动态地选择不同的策略类,从而实现不同的行为。用一句话来说,就是:“准备一组算法,并将每一个算法封装起来,使得它们可以互换”。

这个模式涉及到三个角色:

  ●  环境(Context)角色:持有一个Strategy的引用。

  ●  抽象策略(Strategy)角色:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。

  ●  具体策略(ConcreteStrategy)角色:包装了相关的算法或行为。

2 认识策略模式

  策略模式的重心:

  策略模式的重心不是如何实现算法,而是如何组织、调用这些算法,从而让程序结构更灵活,具有更好的维护性和扩展性。

  算法的平等性:

  策略模式一个很大的特点就是各个策略算法的平等性。对于一系列具体的策略算法,大家的地位是完全一样的,正因为这个平等性,才能实现算法之间可以相互替换。所有的策略算法在实现上也是相互独立的,相互之间是没有依赖的。

  所以可以这样描述这一系列策略算法:策略算法是相同行为的不同实现。

  运行时策略的唯一性:

  运行期间,策略模式在每一个时刻只能使用一个具体的策略实现对象,虽然可以动态地在不同的策略实现中切换,但是同时只能使用一个。

  公有的行为:

  经常见到的是,所有的具体策略类都有一些公有的行为。这时候,就应当把这些公有的行为放到共同的抽象策略角色Strategy类里面。当然这时候抽象策略角色必须要用Java抽象类实现,而不能使用接口。

3 策略模式的优点和缺点

策略模式的优点

  (1)策略模式提供了管理相关的算法族的办法。策略类的等级结构定义了一个算法或行为族。恰当使用继承可以把公共的代码移到父类里面,从而避免代码重复。

  (2)使用策略模式可以避免使用多重条件(if-else)语句。多重条件语句不易维护,它把采取哪一种算法或采取哪一种行为的逻辑与算法或行为的逻辑混合在一起,统统列在一个多重条件语句里面,比使用继承的办法还要原始和落后。

策略模式的缺点

  (1)客户端必须知道所有的策略类,并自行决定使用哪一个策略类。这就意味着客户端必须理解这些算法的区别,以便适时选择恰当的算法类。换言之,策略模式只适用于客户端知道算法或行为的情况。

  (2)由于策略模式把每个具体的策略实现都单独封装成为类,如果备选的策略很多的话,那么对象的数目就会很可观。

4 策略模式适用场景:

        (1) 当一个系统中存在多个类只有它们的行为或算法不同时。

        (2) 当一个类定义了多种行为,而这些行为在这个类的操作中以多个条件语句的形式出现,可以将相关的条件分支移入它们各自的策略类中,以替换这些条件语句。

        (3) 当系统需要动态地在几种算法中选择一种时,如根据不同的配置、用户选择或者环境条件等。

5 策略模式的最佳实践(重点)

        策略模式是一个非常简单且常用的设计模式,策略模式最常见的作用就是解决代码中冗长的 if-else 或 switch 分支判断语句;同时也可以提高程序的扩展性和灵活性,避免代码重复

策略模式的使用包含三部分:策略的定义、创建和使用。

5.1 策略的定义、创建和使用

5.1.1 策略的定义

        策略类的定义比较简单,包含一个策略接口和一组实现这个接口的策略类。所有的策略类都实现相同的接口,所以,客户端代码基于接口而非实现编程,可以灵活地替换不同的策略。

public interface Strategy {void algorithmInterface();
}public class ConcreteStrategyA implements Strategy {@Overridepublic void  algorithmInterface() {//具体的算法...}
}public class ConcreteStrategyB implements Strategy {@Overridepublic void  algorithmInterface() {//具体的算法...}
}

        如上述代码所示,定义了一个策略接口 Strategy,具体的策略实现类都实现 Strategy 接口,并重写 algorithmInterface 方法,最后客户端根据不同的策略即可调用不同实现类的 algorithmInterface 方法。

5.1.2 策略的创建

        可以将创建策略的代码逻辑抽象到工厂类中,提前在工厂类创建好所有策略类,缓存在 Map 中。Map 的 key 为策略类型,value 为具体的策略实现类。当需要使用策略时根据 type 去 Map 中 get 即可获取到相应的策略实现类。

public class StrategyFactory {// Map 的 key 为策略类型,value 为具体的策略实现类private static final Map<String, Strategy> strategies = new HashMap<>();// 提前创建好所有策略类,缓存到 Map 中static {strategies.put("A", new ConcreteStrategyA());strategies.put("B", new ConcreteStrategyB());}// 需要使用策略时根据 type 去 Map 中 get 即可获取到相应的策略实现类public static Strategy getStrategy(String type) {if (type == null || type.isEmpty()) {throw new IllegalArgumentException("type should not be empty.");}return strategies.get(type);}
}

        这里重点在于使用”查表法”代替了大量的分支判断,即每次根据 type 去 Map 中获取,省略了大量的 if-else。

5.1.3 策略的使用

        具体使用 A、B、C 何种策略,在具体的场景,可以会根据系统的配置来选择。可以从配置文件中读取出配置,然后传递给策略工厂类 StrategyFactory 的 getStrategy 方法即可获取到相应的策略类。最后调用策略类的 algorithmInterface 方法去执行代码逻辑。

代码如下所示,省略了从配置文件中读取配置的流程。

// 根据 type 的不同,执行不同分支的代码逻辑,
private void process(String type){Strategy strategy = StrategyFactory.getStrategy(type);
// 调用策略类的 algorithmInterface 方法去执行代码逻辑
strategy.algorithmInterface();
}

5.1.4 当前设计是否容易扩展性

        最原始的 process 方法中,首先会判断各种 type,然后执行不同类型的代码逻辑。如果需要扩展新的 D、E、F 类型,需要大量修改 process 方法。

优化后,如果需要扩展新的 D、E、F 类型,流程如下:

  • 定义相应的 D、E、F 类型的策略实现类
  • 提前在策略工厂类 StrategyFactory 中创建相应的策略实现类,并添加到 Map 中
  • 客户端代码不用进行任何改动,即:process 方法不需要进行改动

        优化后的代码相对来说职责更加单一,且对调用方非常友好。调用方代码不需要任何改动即可使用新的策略。要做的可能就是在配置文件中配置新的策略即可。

5.2 有状态的策略类不能提前创建

        在策略类的创建部分,在类初始化时,将所有的策略类提前创建好,存放在 Map 中。当需要使用策略时根据 type 去 Map 中 get 即可获取到相应的策略实现类。

        假设策略类是有状态的,每次获取策略对象时,都要求创建新的策略类。此时,就不能使用 Map 缓存的方式来优化代码结构了。可以使用如下方式实现策略工厂类:

public class StrategyFactory {public static Strategy getStrategy(String type) {if (type == null || type.isEmpty()) {throw new IllegalArgumentException("type should not be empty.");}if (type.equals("A")) {return new ConcreteStrategyA();} else if (type.equals("B")) {return new ConcreteStrategyB();}return null;}
}

上述代码又退化回了 if-else 嵌套,当然也可以优化为 switch-case 的设计。

        极客时间-王争老师的《设计模式之美》课程第 60 节最后的课堂讨论中留下了一道题目:在策略工厂类中,如果每次都要返回新的策略对象,我们还是需要在工厂类中编写 if-else 分支判断逻辑,那这个问题该如何解决呢?

        笔者看到文末大家的回答,点赞数最高的评论是:仍然可以用查表法,只不过存储的不再是实例,而是class,使用时获取对应的class,再通过反射创建实例。

        反射在这里应该是可以实现,但是笔者感觉不是非常灵活,假设策略实现类需要在这里调用一些有参构造器,且不同的策略类的有参构造器需要传入的参数不同,那么反射实现起来不是非常灵活。

        例如 ConcreteStrategyA 的构造器需要传入 age,ConcreteStrategyB 的构造器需要传入 date。对于这样的 case,反射不太好实现,如果实现出来,也是一对 if-else 分支判断。

        文末也没有其他令笔者眼前一亮的回答,反倒是笔者在阅读 Flink 源码的过程中,发现了一个笔者感觉比较优秀的解决方案,下面就到了秀操作环节。

5.2.1 秀操作

首先分析一个问题来源:

  • 对于无状态的策略类,将所有的策略类提前创建好,存放在 Map 中。当需要使用策略时根据 type 去 Map 中 get 即可获取到相应的策略实现类。
  • 对于有状态的策略类,不能提前创建所有的策略类,所以没办法提前创建好将其存放在 Map 中

换种思路:

  • 给每个具体的策略类创建相应的策略类工厂。例如 ConcreteStrategyA 的工厂为 StrategyFactoryA, ConcreteStrategyB 的工厂为 StrategyFactoryB。
  • 虽然没办法提前创建好策略类放到 Map 中,但是可以将策略类的工厂类提前创建好放到 Map 中。根据传入的 type 就可以从 Map 中获取相应策略类的工厂类,然后执行工厂类的 create 方法即可创建出相应的策略类。

根据上述思路,实现相应代码。

首先定义策略工厂接口,并分别实现策略 A 和策略 B 的工厂类:

// 策略工厂接口
public interface StrategyFactory {Strategy create();
}// 策略 A 的工厂类,用于创建策略 A
public class StrategyFactoryA implements StrategyFactory{@Overridepublic Strategy create() {return new ConcreteStrategyA();}
}// 策略 B 的工厂类,用于创建策略 B
public class StrategyFactoryB implements StrategyFactory{@Overridepublic Strategy create() {return new ConcreteStrategyB();}
}

对外开放的工厂实现如下:

public class Factory {// Map 的 key 为策略类型,value 为 策略的工厂类private static final Map<String, StrategyFactory> STRATEGY_FACTORIES = new HashMap<>();static {// 将各种实现类的工厂提前创建好放到 Map 中STRATEGY_FACTORIES.put("A", new StrategyFactoryA());STRATEGY_FACTORIES.put("B", new StrategyFactoryB());}public static Strategy getStrategy(String type) {if (type == null || type.isEmpty()) {throw new IllegalArgumentException("type should not be empty.");}// 根据 type 获取对应的策略工厂StrategyFactory strategyFactory = STRATEGY_FACTORIES.get(type);// 调用具体工厂类的 create 方法即可创建出相应的策略类return strategyFactory.create();}
}

        Factory 类中定义了 Map,Map 的 key 为策略类型,value 为 策略的工厂类。Factory 类初始化时,将各种实现类的工厂提前创建好放到 Map 中。

        Factory 类的静态方法 getStrategy 用于根据 type 创建相应的策略类,getStrategy 方法根据 type 从 Map 中获取 type 对应的策略类的工厂,调用具体工厂类的 create 方法即可创建出相应的策略类。

当策略类的构造方法比较复杂也没关系,封装在策略类相应的工厂中即可。

        旧方案对于每次要创建新策略类的场景,要搞一堆 if-else 分支判断,上述流程使用 Map 优化了 if-else 分支判断逻辑。但带来了一个新的问题,即:创建出了很多类,相比之前的实现来讲,多了 StrategyFactoryA 和 StrategyFactoryB 类。

        为了代码的简洁,可以利用 Java8 的 lambda 表达式将 StrategyFactoryA 和 StrategyFactoryB 类优化掉。截取上述部分代码实现:

// 策略工厂接口
public interface StrategyFactory {Strategy create();
}// 策略 A 的工厂类,用于创建策略 A
public class StrategyFactoryA implements StrategyFactory{@Overridepublic Strategy create() {return new ConcreteStrategyA();}
}Map<String, StrategyFactory> STRATEGY_FACTORIES = new HashMap<>();
// 将各种实现类的工厂提前创建好放到 Map 中
STRATEGY_FACTORIES.put("A", new StrategyFactoryA());

上述代码使用 lambda 优化后:

// 策略工厂接口
public interface StrategyFactory {Strategy create();
}Map<String, StrategyFactory> STRATEGY_FACTORIES = new HashMap<>();
// 将各种实现类的工厂提前创建好放到 Map 中
STRATEGY_FACTORIES.put("A", () -> new ConcreteStrategyA());

关于 lambda 这里就不多解释了。lambda 表达式还能优化为 Java8 的方法引用,代码如下所示:

STRATEGY_FACTORIES.put(“A”, ConcreteStrategyA::new);

5.2.1 小结

把上述整个代码的最终版贴在这里:

// 策略工厂接口
public interface StrategyFactory {Strategy create();
}public class Factory {// Map 的 key 为策略类型,value 为 策略的工厂类private static final Map<String, StrategyFactory> STRATEGY_FACTORIES = new HashMap<>();static {// 将各种实现类的工厂提前创建好放到 Map 中STRATEGY_FACTORIES.put("A", ConcreteStrategyA::new);STRATEGY_FACTORIES.put("B", ConcreteStrategyB::new);}public static Strategy getStrategy(String type) {if (type == null || type.isEmpty()) {throw new IllegalArgumentException("type should not be empty.");}// 根据 type 获取对应的策略工厂StrategyFactory strategyFactory = STRATEGY_FACTORIES.get(type);// 调用具体工厂类的 create 方法即可创建出相应的策略类return strategyFactory.create();}
}

        代码量相比之前的策略类可以共享的代码设计来讲,只是增加了一个 StrategyFactory 接口的设计,所以整体代码也是非常简洁的。

5.3 策略模式在项目中的真实实现

5.3.1 实际需求

        智慧停车的功能模块,由于各个地方项目上用到的第三方车厂厂商的设备不一致(如 科托,捷顺,红门,零壹等)对应需要要对接的接口也比较多;虽然对接的车厂不一样,但是在我们业务系统中要做的事都是一样的;如都需要通过我们app来开通月卡,续费,扫码出场,车辆预约等;只是这些功能的实现需要去调用不同车厂的提供的相似功能的接口去实现;

        该系统需要动态地在几种算法中选择一种时,如根据不同的配置来选择;因此选择策略模式是比较合适的;这样可以程序结构更灵活,具有更好的维护性和扩展性(后续如果要加入其他的第三方车厂就很方便拓展了);策略类的等级结构定义了一个算法或行为族。恰当使用继承可以把公共的代码移到父类里面,从而避免代码重复.

5.3.2 实践

策略的定义

        策略类的定义比较简单,包含一个策略接口和一组实现这个接口的策略类。所有的策略类都实现相同的接口,所以,客户端代码基于接口而非实现编程,可以灵活地替换不同的策略。

public interface ThirdParkVersionService {/**--------------------------------科托车场厂商相关接口对接--------------------------------------------*/KeyTopResp<CarsCardVOs> getCarCardList(CarCardDTOs dto);/**--------------------------------捷顺车场厂商相关接口对接--------------------------------------------*/JsstResp<DelaybymodeVO> delaybymode(DelaybymodeDTO delaybymodeDTO);
}
/*** @Description 聚合不同厂商的默认实现 * @version1.0 利用接口默认方法进行空实现,防止下层接口需要实现过多方法*/
public interface ThirdParkDefaultService extends ThirdParkVersionService{static String getThirdConfigKey(String thirdParkId,Long projectId){return projectId.toString().concat(thirdParkId);}/**--------------------------------------科托厂商接口默认实现-------------------------------------------------*//*** @Description:* @Param: [carCardDTOs]* @return: com.bzcst.bop.iot.car.manager.entity.common.response.KeyTopResp<com.bzcst.bop.iot.car.manager.entity.keytopvo.CarsCardVOs>* @Author: xiongguoshuang* @Exception: 获取固定车列表* @Date: 2022/5/9 13:51*/@Overridedefault KeyTopResp<CarsCardVOs> getCarCardList(CarCardDTOs dto) {return null;}
......................}public interface KeyTopService extends ThirdParkDefaultService{//实现科托相关接口}
public interface JsstService extends ThirdParkDefaultService {// 实现捷顺相关接口
}@Component(ThirdParkConstants.BUSINESS_TYPE_KEYTOP_VER_FIVE)
public class KeyTopFiveServiceImpl implements KeyTopService{//接口实现类
}
@Component(ThirdParkConstants.BUSINESS_TYPE_JSST_VER_THIRD)
public class JsstThirdServiceImpl implements JsstService {//接口实现类
}
策略的创建

        可以将创建策略的代码逻辑抽象到工厂类中,提前在工厂类创建好所有策略类,缓存在 Map 中。Map 的 key 为策略类型,value 为具体的策略实现类。当需要使用策略时根据 type 去 Map 中 get 即可获取到相应的策略实现类。这里的Map 通过服务类自定义的类名自动注入实现

@Component
public class ThirdParkFactorySelector {@Resourceprivate Map<String, ThirdParkFactoryService> handlers;public ThirdParkFactoryService thirdParkFactoySelector(String providerCode) {ThirdParkFactoryService thirdParkFactoryService = handlers.get(providerCode);return thirdParkFactoryService;}
}@Component
public class ThirdParkFactoryServiceSelector {@ResourceDeviceProviderApiService deviceProviderApiService;@ResourceCarYardConfigService carYardConfigService;@ResourceThirdParkFactorySelector thirdParkFactorySelector;/*** 根据车场ID获取对应的车场厂商实例 调用需要实现的业务逻辑*/public ThirdParkFactoryService getThirdParkFactoryService(Long carParkId) {CarYardConfig carYardConfig = getCarYardConfigByParkId(carParkId);Long partnerId = carYardConfig.getPartnerId();if(partnerId == null){throw new BusinessException("根据carParkId未找到对应的partnerId");}DeviceProviderVO provider = deviceProviderApiService.get(partnerId);return thirdParkFactorySelector.thirdParkFactoySelector(provider.getCode());}}
策略的使用

        具体在业务代码中调用那个车厂厂商的接口,是根据系统配置表中具体的配置来选择,从数据表中读取具体的配置,调用getThirdParkFactoryService获取对应的策略类;最后调用策略类的中具体的方法去执行代码逻辑。

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

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

相关文章

Flutter学习笔记

此篇文章用来记录学习Flutter 和 Dart 相关知识 零.Dart基本数据类型 Dart 是一种静态类型的编程语言&#xff0c;它提供了一系列基本数据类型&#xff0c;用于存储和操作不同种类的数据。以下是 Dart 中的一些基本数据类型以及它们的详细介绍&#xff1a; 1. 整数类型&#…

爱国者的润学日记-十月

首先需要科学的准备面试和润。如何进行科学的准备工作呢&#xff1f; 高效的按照面试考察内容进行针对性训练&#xff0c;按 Machine-learning-interview准备保证处于专注的心态&#xff0c;如今互联网娱乐发达&#xff0c;之前即使比赛时我也是一边比赛一边看视频。之后准备面…

L1-035 情人节 c++解法

题目再现 以上是朋友圈中一奇葩贴&#xff1a;“2月14情人节了&#xff0c;我决定造福大家。第2个赞和第14个赞的&#xff0c;我介绍你俩认识…………咱三吃饭…你俩请…”。现给出此贴下点赞的朋友名单&#xff0c;请你找出那两位要请客的倒霉蛋。 输入格式&#xff1a; 输入…

3.2.OpenCV技能树--二值图像处理--图像腐蚀与膨胀

文章目录 1.文章内容来源2.图像膨胀处理2.1.图像膨胀原理简介2.2.图像膨胀核心代码2.3.图像膨胀效果展示 3.图像腐蚀处理3.1.图像腐蚀原理简介3.2.图像腐蚀核心代码3.3.图像腐蚀效果展示 4.易错点总结与反思 1.文章内容来源 1.题目来源:https://edu.csdn.net/skill/practice/o…

探秘小米增程汽车与仿生机器人的未来:AI大模型的潜在影响及苹果iPhone15Pro发热问题解决之道

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…

现货白银图表分析的依据

现货白银的行情图表分析其实与股票的差不多&#xff0c;投资者可以结合均线、k线的变化&#xff0c;来分析实时的行情走势。当走势图的均线呈多头排列&#xff0c;即短期、中期、长期均线依次从上到下排列并向右上方运行&#xff0c;且白银价格沿各均线向右上方拉升&#xff0c…

一文熟练使用python修改Excel中的数据

使用python修改Excel中的内容 1.初级修改 1.1 openpyxl库的功能&#xff1a; openpyxl模块是一个读写Excel 2010文档的Python库&#xff0c;如果要处理更早格式的Excel文档&#xff0c;需要用到额外的库&#xff0c;例如Xlwings。openpyxl是一个比较综合的工具&#xff0c;能…

访问Apache Tomcat的虚拟主机管理页面

介绍 通过Tomcat Host Manager应用可以创建、删除、管理Tomcat内的虚拟主机&#xff08;virtual hosts&#xff09;。该应用是Tomcat安装的一部分&#xff0c;默认在<Tomcat安装目录>/webapps/host-manager&#xff1a; 配置用户名、密码、角色 要访问Host Manager应…

鉴源实验室 | AUTOSAR SecOC:保障汽车通信的安全

作者 | 沈平 上海控安可信软件创新研究院汽车网络安全组 来源 | 鉴源实验室 社群 | 添加微信号“TICPShanghai”加入“上海控安51fusa安全社区” 在现代汽车行业中&#xff0c;随着电子控制单元&#xff08;ECUs&#xff09;的普及以及车与车之间通信的不断增加&#xff0c;确…

《spring security in action》读书笔记

1. why spring security 是强大的高度可定制的 身份验证 和 访问控制 应用级框架。 常见的漏洞包含&#xff1a; 身份验证失效&#xff0c;会话固定&#xff0c;跨站脚本xss请求伪造&#xff0c;CSRF注入敏感数据泄漏缺乏方法访问控制。 身份验证失效&#xff1a; 不能仅仅验…

[尚硅谷React笔记]——第3章 React应用(基于React脚手架)

目录&#xff1a; react脚手架创建项目并启动react脚手架项目结构一个简单的Hello组件样式的模块化功能界面的组件化编码流程&#xff08;通用&#xff09;组件的组合使用-TodoList 1.react脚手架 xxx脚手架: 用来帮助程序员快速创建一个基于xxx库的模板项目 包含了所有需…

/lib64/libstdc++.so.6: version `GLIBCXX_3.4.21‘ not found (required by

在某项目中遇到下面的错误&#xff0c; ./model2trt_v2: /lib64/libstdc.so.6: version GLIBCXX_3.4.21 not found (required by ./model2trt_v2) ./model2trt_v2: /lib64/libstdc.so.6: version GLIBCXX_3.4.21 not found (required by ../../../lib/linux_lib/libcuda_utils…

QT之可自由折叠和展开的布局

介绍和功能分析 主要是实现控件的折叠和展开&#xff0c;类似抽屉控件&#xff0c;目前Qt自带的控件QToolBox具有这个功能&#xff0c;但是一次只能展开一个&#xff0c;所以针对自己的需求可以自己写一个类似的功能&#xff0c;这里实现的方法比较多&#xff0c;其实原理也比较…

2023.10.02 win7x64sp1下Navicat_Premium15_x86连接Oracle_10g(安装在win2003x86)

Oracle_10g安装在这个版本的系统里: Microsoft Windows [版本 5.2.3790] 这个win2003_x86(分配内存1G)安装在vmware虚拟机里. 安装包文件名为:oracle 10g_win32.zip 大小约624 MB (655,025,354 字节) 安装完毕后,tcp1521端口应该开放: Microsoft Windows [版本 5.2.3790]…

Matlab参数估计与假设检验(举例解释)

参数估计分为点估计和区间估计&#xff0c;在matlab中可以调用namefit()函数来计算参数的极大似然估计值和置信区间。而数据分析中用得最多的是正态分布参数估计。 例1 从某厂生产的滚珠中抽取10个&#xff0c;测得滚珠的直径&#xff08;单位&#xff1a;mm&#xff09;为x[…

Git入门详解

Git入门详解 本文承接上文 Git入门简介 并做了内容扩充。本文讲述Git工具的安装、配置及使用友情参考链接&#xff1a;https://gitee.com/all-about-git 1. Git安装 安装官网&#xff1a;https://git-scm.com/安装过程如下&#xff1a; 双击.exe默认安装即可 2. Git配置 …

云安全之下一代防火墙介绍

防火墙的概念 下一代防火墙&#xff08;Next Generation Firewall&#xff0c;NGFW&#xff09;是一种可以全面应对应用层威胁的高性能防火墙。通过深入洞察网络流量中的用户、应用和内容&#xff0c;并借助全新的高性能单路径异构并行处理引擎&#xff0c;NGFW能够为用户提供…

掌握核心技巧就能创建完美的目录!如何在Word中自动创建目录

目录是Word布局的一个重要因素&#xff0c;尤其是在编写较长的文档时。那么&#xff0c;你如何在你的作品中添加目录呢&#xff1f;在这篇文章中&#xff0c;我将分享一些基于Word2016自动创建目录的经验。希望它能或多或少地帮到你。 自动创建目录 1、输入目录文本的名称&am…

【Redis】Redis中的数据结构和内部编码

Redis中的数据结构和内部编码 type命令实际返回的就是当前键的数据结构类型&#xff0c;它们分别是&#xff1a;string&#xff08;字符串&#xff09;、list&#xff08;列表&#xff09;、hash&#xff08;哈希&#xff09;、set&#xff08;集合&#xff09;、zset&#xf…

第二证券:华为全液冷超充上线,高压快充概念爆发,双杰电气等涨停

受华为全液冷超充上线消息提振&#xff0c;高压快充概念9日盘中强势拉升&#xff0c;到发稿&#xff0c;双杰电气、永贵电器“20cm”涨停&#xff0c;英可瑞、易事特涨超13%&#xff0c;伊戈尔、协鑫能科、宝馨科技、日丰股份等涨停&#xff0c;万祥科技、星云股份涨近8%。 消…