软件设计不是CRUD(7):低耦合模块设计实战——组织机构模块(中)

news2024/11/16 4:39:09

======== 接上文《软件设计不是CRUD(6):低耦合模块设计实战——组织机构模块(上)》

组织机构功能是应用系统中常见的业务功能之一,但是不同性质、不同行业背景、不同使用场景的应用系统对组织机构功能的要求可能完全不一样。所以使用这样的功能对低耦合模块设计进行示例性的讲解是比较具有代表性的。在后续的几篇文章中,我们会首先进行示例的详细讲解,然后再基于这个示例进行理论讲解。

4、做一个默认实现

在这里插入图片描述

如上图所示,由于组织机构模块可以有多种实现方式;例如可以研发一套基于本地关系型数据库的实现,也可以研发一套基于远程调用的实现,还可以研发一套基于ES搜索引擎的实现。这里本文来演示一套基于本地关系型数据库的实现,这套实现作为产品研发团队提供的默认组织机构实现提供给上层项目团队/二次开发团队使用(由于篇幅有限,本文尽可能演示其中的关键代码),二次开发团队如果不满意这套实现,可以基于这套实现进行调整也可以对这套实现进行整体替换,但都不会影响其它模块对该模块的调用——因为其它“可以看到”组织机构的模块,依赖的都是组织机构模块提供的接口。

4.1、对接口的补充说明并建立工程脚手架

如上图所示,组织机构模块基于本地关系型数据库的实现,工程名被定为simple-org-local(pom文件中定义为(simple-org-local-starter),使用者可以通过在应用程序POM文件中设定simple-org-local-starter依赖的方式,将组织机构模块基于本地数据库的实现引入到应用程序中运行。另外要说明的是,笔者在实现过程中针对之前SDK设计考虑不周的问题,对组织机构接口做了细微调整,在进行正式介绍前,需要向读者先说明这些细微调整:

  • 在OrganizationModuleRegister接口和UserMappingModuleRegister接口中,增加了返回具体模型类型的方法。这是因为在实现过程中发现如果二次开发团队需要依赖当前的默认实现调整一些模型的字段情况,则需要提供一个指定具体模型类的方法:
// 对OrganizationModuleRegister接口进行的调整
public interface OrganizationModuleRegister <O extends Organization> extends Ordered {
  // ......
  // 该方法用于返回具体要转换的组织机构class类型
  public Class<? extends O> getOrgClass();
  // ......
}

// 对UserMappingModuleRegister接口进行的调整
public interface UserMappingModuleRegister <M extends UserMapping> extends Ordered {
  // ......
  // 该方法用于返回具体要转换的组织机构-用户class类型
  public Class<? extends M> getMappingClass();
  // ......
}
  • 提供支持简单列表结构的组织机构模型。这是因为在实现过程中发现有的项目团队可能不需要组织机构支持树形结构,只需要提供对单纯二维表结构的支持。最简单的方式就是将组织机构模型,分为两个有继承关系的独立接口:
// Organization模型接口不具有树形结构的描述特点
public interface Organization {
  // 组织机构类型
  public String getType();
  // 在组织机构类型下,唯一的组织机构业务编码
  public String getCode();
  // 组织机构的中文名
  public String getName();
}

// ...................

// 继承他的TreeOrganization模型接口,才具有描述树形结构的要求
public interface TreeOrganization extends Organization {
  // 组织机构携带的下级组织机构信息(组织机构特性字段)
  public <O extends Organization> List<O> getKids();
  public void setKids(List<? extends Organization> kids);
  // 组织机构直接携带的人员绑定信息(组织机构特性字段)
  public <M extends UserMapping> List<M> getUsers();
  public void setUsers(List<? extends UserMapping> users);
  // 获取当前节点可能的父级节点类型(注意:可能没有)
  public String getParentType();
  // 获取当前节点可能的父级节点编号(注意:可能没有)
  public String getParentCode();
}

以上调整都是对上文介绍的组织机构接口的细微调整,都不会影响读者对于实现的理解,也不会影响本示例所要体现出的低耦合设计思想。接着我们就可以进入正题了,首先给出这个本地数据库实现的具体脚手架(以及脚手架中重要的实现类),然后再对脚手架中重要的实现类进行详细介绍。
在这里插入图片描述
其它一些关注度不会太高的代码包还有基于JPA的数据库模型entity包,以及数据库操作接口repository包,还有工具包utils(实际工作中,这些工具包会统一放置在一些更下层的模块中)等等。读者可以在作者的下载空间中,对这些详细代码进行下载。

TODO 代码还没有上传

注意:spring.factories文件的设置方式是spring-boot 2.7 之前版本的设置方式,在spring-boot 2.7及后续版本推荐/要求使用org.springframework.boot.autoconfigure.AutoConfiguration.imports文件的设置方式。读者自行进行脚手架验证时,需要注意这个细节。

4.2、建立和注册具体的业务模型

上文已经多次提到,在组织机构模块的接口定义中,关于“组织机构具体的模型结构”这件事情,只是进行了一个抽象描述,既是:只要具有类型、类型下唯一编号的模型,就可以是一种组织机构(实现Organization接口)。如果这种组织机构需要支持树形接口,则另外需要规定父级组织机构的信息和下级组织机构的信息(实现TreeOrganization接口)。

那么在具体的实现中就需要对组织机构的模型结构进行详细描述了,由于在这个默认实现中,组织机构类型是需要支持树形结构的,所以需要实现TreeOrganization接口:
在这里插入图片描述
DefaultOrg类的详细代码,如下所示:

/**
 * 这是标准产品提供的默认组织机构实现,其中默认可以关联各种类型的下级组织机构和各种类型的用户
 * 为了简化代码,这里使用了lombok组件,以降低不必要的代码
 * @author yinwenjie
 */
@Getter
@Setter
public class DefaultOrg implements TreeOrganization {
  public static final String DEFAULT_ORG = "default";
  private String id;
  // 组织机构类型
  private String type = DEFAULT_ORG;
  // 业务编号
  private String code;
  // 组织机构上级组织机构编号
  private String parentCode;
  // 组织机构上级组织机构类型
  private String parentType;
  // 中文名
  private String name;
  // 组织机构完整称呼(组织机构特性字段)
  private String mainName;
  // 组织机构邮件地址(组织机构特性字段)
  private String mail;
  // 组织机构电话信息(组织机构特性字段)
  private String phone;
  // 组织机构携带的下级组织机构信息(组织机构特性字段)
  private List<? extends Organization> kids;
  //组织机构直接携带的人员绑定信息(组织机构特性字段)
  private List<? extends UserMapping> users;
}

当然,除了默认组织机构的详细结构描述外,组织机构的默认实现中还实现了一个默认的用户关联信息。该关联信息的数据结构,实现了UserMapping接口,代码如下所示:

/**
 * 这是默认的组织机构、用户关联信息
 * (实际上生产系统的情况下,这个关联用户结构应该由上层用户模块进行定义)
 * @author yinwenjie
 */
@Getter
@Setter
public class DefaultMappingUser implements UserMapping {
  public static final String DEFAULT_MAPPING_ORG = "default";
  // 唯一的用户账号信息
  private String account;
  // 用户中文姓名信息
  private String describer;
  // 用户昵称信息
  private String name;
  // 用户年龄(特异性业务字段)
  private Integer age;
  // 用户检查次数信息(特异性业务字段)
  private BigDecimal checkPoint;
  // 业务申请次数(特异性业务字段)
  private Integer requestNum;
}

注意事项:类似的这种用户关联信息,在实际工作基本不会放置在本模块的实现中,而是直接放置在需要和组织机构信息建立关联的上层模块中(例如上层的管理员用户模块、监控员用户模块),这样做有几个原因:

  • 重用上层模块的模型结构代码:上层模块中需要和组织机构信息建立关联的信息结构,只要直接实现UserMapping接口,就可以将模型结构关联到组织机构模块中,不需要上层模块做额外的代码增加,也不会发生因为上层模块需要将信息关联到组织机构,而改变自身模型结构的情况。

  • 将关联关系的具体实现交给上层模块,保证在上层模块对下层模块透明的情况下,支持更多关联模块的扩展:本系列文章中多次提到,为了保证系统中模块分层的稳定性,上层模块是对下层模块透明的。换句话说下层模块是不知道其上层有什么模块,所以下层模块当然无法知晓上层有哪些模块需要建立和组织机构的关联。
    在这里插入图片描述

以下代码是具体的组织机构模型和用户关联模型的注册实现(DefaultOrg和DefaultMappingUser两个模型):

// 注册组织机构模型(默认组织机构)
public class DefaultOrganizationModuleRegister implements OrganizationModuleRegister<DefaultOrg> {  
  // 默认的组织机构类型,排序优先度很高
  @Override
  public int getOrder() {
    return 1;
  }
  @Override
  public String type() {
    return DefaultOrg.DEFAULT_ORG;
  }
  // 这里描述json结构到具体DefaultOrg模型的转换
  @Override
  public DefaultOrg transform(JSONObject json) {
    // ......
  }
}

// =============
// 注册示例中需要的组织-用户关联模型
public class DefaultUserMappingModuleRegister implements UserMappingModuleRegister<DefaultMappingUser> {
  // 默认这个模型注册器所转换的对象模型,排序优先级最高
  @Override
  public int getOrder() {
    return 0;
  }
  // 具体描述json结构和用户关联信息的转换
  @Override
  public List<DefaultMappingUser> transform(JSONArray users) {
    // 只有要求转换的用户关联信息,其type都为default的时候,才进行转换,否则报错
    // ......
  }
}

4.3、建立具体的业务行为

建立了具体的业务模型后,现在需要进行具体的业务行为描述。业务行为在本示例中可以简单理解为对业务模型的增、删、改、查操作。这就是组织机构模块中,OrganizationStrategy接口和UserMappingStrategy接口存在的原因。

在这里插入图片描述
注意:以上两个策略接口中的行为只是需要接入组织机构模块统一控制的各种模型行为,不包括某种组织机构类型、某种组织机构-用户关联类型特有的行为。例如,作为组织机构模型来说,一定存在创建、修改和层次结构查询的要求,这些属于对于组织机构模型最基本的要求。另外,不同的组织机构类型还有定制化的行为,例如“组织机构类型A”还允许进行临时禁用操作。而这些针对不同组织机构类型的定制化行为,并不属于OrganizationStrategy接口和UserMappingStrategy接口的控制范围,需要二次开发团队根据自己的需要,自行定义并实现(一般在Service层定义新的接口,且这些接口和实现只存在于项目使用的范围)。

  • 首先是默认组织机构的业务逻辑描述
public class DefaultOrganizationStrategy implements OrganizationStrategy<DefaultOrg> {
  // 默认实现内部使用的数据持久层功能
  @Autowired
  private DefaultOrgRepository orgRepository;
  @Override
  public String type() {
    return DefaultOrg.DEFAULT_ORG;
  }
  // 创建默认组织机构信息
  @Override
  public DefaultOrg create(DefaultOrg org) {
    /*
     * 这里进行默认组织机构的实际创建,过程为:
     * 1、进行和业务相关的边界校验
     * 2、进行数据层的保存
     * 注意,这里不进行监听器的激活,因为它不属于实际的创建过程
     * */
    
    // 1、=============
    // 验证和业务相关的字段
    String mail = org.getMail();
    Validate.notBlank(mail , "创建时,邮件信息必须填写");
    // TODO 还能进行邮件字段的其它业务相关性验证,例如邮件是否合规,是否被使用等
    String name = org.getName();
    Validate.notBlank(name , "创建时,姓名信息必须填写");
    String phone = org.getPhone();
    Validate.notBlank(phone , "创建时,电话信息必须填写");
    
    // 2、=============
    // 转换成默认实现内部使用的数据持久层对象,并进行入库操作
    DefaultOrgEntity orgEntity = this.transform(org);
    this.orgRepository.save(orgEntity);
    // 保存后,数据层会生成一个id
    org.setId(orgEntity.getId());
    return org;
  }
  
  // ...... 其它方法实现过程略
  // 包括必要的查询、修改操作等
}
  • 然后是默认组织机构-用户映射方式的业务逻辑描述
public class DefaultUserMappingStrategy implements UserMappingStrategy<DefaultMappingUser> {
  // 默认实现内部的数据层功能
  @Autowired
  private DefaultMappingUserRepository defaultMappingUserRepository;
  
  @Override
  public int getOrder() {
    return 1;
  }
  @Override
  public String type() {
    return DefaultMappingUser.DEFAULT_MAPPING_ORG;
  }
  // 创建新的组织机构-用户映射信息
  @Override
  public DefaultMappingUser create(DefaultMappingUser userMapping) {
    // 1、========== 边界校验
    Integer age = userMapping.getAge();
    Validate.notNull(age , "创建关联信息时,人员年龄信息必须填写");
    // TODO 当然还可以进行更多的正确性验证
    Integer requestNum = userMapping.getRequestNum();
    Validate.notNull(requestNum , "创建关联信息时,人员申请次数必须填写");
    
    // 2、========== 进行数据层模型转换
    DefaultMappingUserEntity userMappingEntity = this.transform(userMapping);
    this.defaultMappingUserRepository.save(userMappingEntity);
    userMapping.setId(userMappingEntity.getId());
    return userMapping;
  }
  
  // ...... 这里有一些私有方法和其他业务行为
  
  @Override
  public Collection<DefaultMappingUser> queryUsers(String parentType, String parentCode) {
    // 进行边界校验后,就进行数据库查询
    if(StringUtils.isAnyBlank(parentType , parentCode)) {
      return Lists.newArrayList();
    }
    List<DefaultMappingUserEntity> userMappingEntities = this.defaultMappingUserRepository.findByOrgTypeAndOrgCode(parentType, parentCode);
    if(CollectionUtils.isEmpty(userMappingEntities)) {
      return Lists.newArrayList();
    }
    
    // 如果存在查询结果,则转换为对应的业务模型,然后进行输出
    List<DefaultMappingUser> mappingUsers = this.transform(userMappingEntities);
    return mappingUsers;
  }
}

注意:和UserMapping接口、UserMappingModuleRegister接口类似,在实际工作中对于UserMappingStrategy接口的实现也会交给应用系统中处于组织机构模块上层的,需要接入组织机构模块建立组织机构-用户关联的模块进行实现。出现这种情况原因在上文中已经介绍过了,这里不再赘述。
在这里插入图片描述

4.4、将业务行为进行间接耦合

在《软件设计不是CRUD(5):耦合度的强弱(下)》文章的描述中,我们对间接耦合的概念进行了详细描述。总结来说,就是模块内部没有业务过程,只负责按照一定的逻辑将业务过程组装起来,且处理逻辑本身是可以变化。

为了使模块的耦合强度下降到间接耦合,我们需要在模块中设计一个描述业务过程组装方式的中间层,让每个业务逻辑能够按照一定的工作顺序运行起来(称为处理逻辑)。在本示例中,我们选择传统编程结构中的Service层作为这个处理逻辑的描述层。

public class OrgServiceImpl implements OrgTreeService {
  // 这里是系统中已经注册的组织机构行为
  @Autowired(required = false)
  private List<? extends OrganizationStrategy<? super Organization>> organizationStrategies;
  // 这里是系统中已注册的组织机构-用户关联行为
  @Autowired(required = false)
  private List<? extends UserMappingStrategy<? super UserMapping>> userMappingStrategies;
  // 这里是系统中已注册的组织机构模型具体的类型描述
  @Autowired(required = false)
  private List<? extends OrganizationModuleRegister<? super Organization>> organizationModuleRegisters;
  // 这里是系统中已注册的需要监听组织机构模块事件变化的具体监听器
  @Autowired(required = false)
  private List<? extends OrgListener> orgListeners;
  
  @Override
  @Transactional
  public Organization create(Organization org) {
    /*
     * 在进行新的组织机构查询时,该service方法中并没有具体的业务逻辑过程
     * 而是描述了一个控制过程,具体的处理逻辑在OrganizationStrategy接口的某个具体实现中进行实现
     * 
     * 1、进行边界校验,只对必要的类型信息进行校验,因为处理类型信息以外,本控制过程不需要关注其它业务字段的校验问题
     * 2、通过类型信息找到对应的处理策略,并检查节点是否已经存在,然后进行正式创建操作
     * 3、进行了组织机构基本信息的添加后,再根据情况确认是否要进行携带的用户关联信息的添加
     * 4、创建成功后,本处理逻辑会触发事件监听,将创建成功后的状态通知到上层需要知晓该模块数据变化的模块
     * */
    
    // 1、================
    Validate.notNull(org , "创建时,需要传入必要的组织机构信息");
    String type = org.getType();
    Validate.notBlank(type , "创建时,必须传入组织机构类型(type)信息");
    String code = org.getCode();
    Validate.notBlank(code , "创建时,必须传入组织机构编号(code)信息");
    Validate.isTrue(!CollectionUtils.isEmpty(organizationStrategies) , "创建时,未发现任何已注册的处理方式");
    // 检验可能存在的父级节点信息
    if(org instanceof TreeOrganization) {
      TreeOrganization treeOrg = (TreeOrganization)org;
      String parentCode = treeOrg.getParentCode();
      String parentType = treeOrg.getParentType();
      OrganizationStrategy<? super Organization> parentOrganizationStrategy = this.findOrganizationStrategy(parentType);
      Validate.isTrue(!StringUtils.isAllBlank(parentCode , parentType) && !StringUtils.isAnyBlank(parentCode , parentType) 
        , "创建时,要么父级信息全部完整填写;要么都不填写");
      // 只有两者都完整,才任务当前节点设定了父级节点,如果有父节点,则要确认父节点
      if(!StringUtils.isAnyBlank(parentCode , parentType)) {
        Organization parentOrganization = parentOrganizationStrategy.queryByCode(parentType, parentCode);
        Validate.notNull(parentOrganization , "无法找到指定的父级节点,请检查");
      }
    }
    
    // 2、================
    // 找到正确的操作策略器,并进行正式的创建处理
    OrganizationStrategy<? super Organization> currentOrganizationStrategy = this.findOrganizationStrategy(type);
    Validate.notNull(currentOrganizationStrategy , "创建时,未发现正确的已注册的处理方式(%s)" , type);
    // 验证要添加的节点,是否已经存在
    Organization exsitOrg = currentOrganizationStrategy.queryByCode(type, code);
    Validate.isTrue(exsitOrg == null , "创建时,发现指定类型下的组织机构编号已经被使用,请检查");
    Organization result = currentOrganizationStrategy.create(org);
    Validate.notNull(result , "创建成功后的结果不能为空");
    
    // 3、================
    if(org instanceof TreeOrganization) {
      TreeOrganization treeOrg = (TreeOrganization)org;
      List<? extends UserMapping> users = treeOrg.getUsers();
      if(!CollectionUtils.isEmpty(users)) {
        for (UserMapping userMapping : users) {
          String userMappingType = userMapping.getType();
          Validate.notBlank(userMappingType , "创建时,未传入用户映射类型信息");
          UserMappingStrategy<? super UserMapping> currentUserMappingStrategy = this.findUserMapping(userMappingType);
          Validate.notNull(currentUserMappingStrategy , "创建时,未找到正确的用户映射类型处理策略");
          currentUserMappingStrategy.create(userMapping);
        }
      }
    }
    
    // 4、================
    // 一旦创建成功,则事件会被触发
    if(!CollectionUtils.isEmpty(orgListeners)) {
      for (OrgListener orgListener : orgListeners) {
        orgListener.onCreated(result);
      }
    }
    return result;
  }
  
  // ......
  // 还实现了诸如树形结构查询的方法,请下载代码自行阅读
  // ......
}

从以上的实现代码来看,代码中不是描述某一种具体的组织机构类型应该怎么进行业务属性相关的边界校验、新增入库或者字段修改,而是描述了一种处理过程:先进行业务无关的边界校验,再寻找正确的组织机构处理策略并调用具体的创建过程,然后再调用正确的组织机构-用户映射创建过程,最后触发事件监听。这样一来,定义不同的类型的组织机构和不同类型的组织机构-用户映射关系,就可以基于两个不相交的“组织机构类型”维度和“组织机构-用户映射类型”维度形成无数多条业务逻辑分支。

另外需要注意的是,在本示例中我们将控制逻辑放置在组织机构本地数据库实现(的脚手架)中,主要原因是为了方便讲解顺序。实际的工作中,类似这种控制逻辑的具体实现类(OrgServiceImpl)一般会被放置在接口定义的脚手架中(这里就是org-sdk),因为这种控制逻辑和技术人员做那种具体的实现是没有关系的。
在这里插入图片描述

  • 为了便于读者理解这种控制耦合,上图将组织机构中两个不相交的维度映射成X-Y轴的平面坐标系,并进行控制逻辑的相交表述——但实际上这并不是“维度相交”的定义,只是为了便于读者进行阅读理解才这样进行坐标系映射。后文中,我们将详细描述什么叫做业务维度相交、

  • 因为间接耦合在本示例中是通过一套固定的逻辑过程被关联上的,所以有一种可能是由于这套逻辑过程相对于使用方出现了很大差异,使用方需要直接改变这套固定的处理逻辑过程。这种情况当然是可能出现的,解决办法也只有推荐使用方重新替换service层的实现(只是逻辑过程无法满足的情况),或者直接替换整个组织机构模块的实现(逻辑过程、业务维度都无法满足的情况)。这种场景一般认为组织机构的业务屈服度未达到设计期望,关于业务屈服度的介绍将在后文进行展开。

在这里插入图片描述

上图我们汇总了目前组织机构模块的接口定义和本地数据库默认实现。后文我们将介绍,这个组织机构默认的本地数据库实现,如果部署到具体的应用中。二次开发团队怎么针对组织机构模块提供的接口定义,进行二次开发工作。

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

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

相关文章

[CTFshow 红包挑战] 刷题记录

文章目录 红包挑战7红包挑战8红包挑战9 红包挑战7 考点&#xff1a;xdebug拓展 源码 <?php highlight_file(__FILE__); error_reporting(2);extract($_GET); ini_set($name,$value);system("ls ".filter($_GET[1])."" );function filter($cmd){$cmd s…

visual studio code 好用的插件

vscode-icons Better comments 该插件对不同类型的注释会附加了不同的颜色&#xff0c;更加方便区分&#xff0c;帮助我们在代码中创建更人性化的注释。 Error Lens Error Lens插件是一款可以检测你编写的代码的语法错误&#xff0c;并且会显示出对语法错误的诊断信息…

中科院分区和JCR分区有什么区别

文章目录 名词解释学科划分不同参考的影响因子不同期刊分区不同期刊分区阈值不同 名词解释 中科院分区&#xff1a;又称“中科院JCR分区”&#xff0c;是中国科学院文献情报中心世界科学前沿分析中心的科学研究成果&#xff0c;期刊分区表数据每年底&#xff08;每年12月中下旬…

基于c++版数据结构基于数组栈改-Python思维总结

##栈部分-&#xff08;叠猫猫&#xff09; ##抽象数据类型栈的定义&#xff1a;是一种遵循先入后出的逻辑的线性数据结构。 换种方式去理解这种数据结构如果我们在一摞盘子中取到下面的盘子&#xff0c;我们首先要把最上面的盘子依次拿走&#xff0c;才可以继续拿下面的盘子&…

Java8流式编程详解

简介 java8提供的流式编程使得我们对于集合的处理不再是临时集合加上各种还能for循环&#xff0c;取而代之的是更加简洁高效的流水线操作&#xff0c;所以笔者就以这篇文章总结一下流式编程中常见的操作。 前置铺垫 后文示例操作中&#xff0c;我们都会基于这个菜肴类的集合…

BUUCTF crypto做题记录(3)新手向

目录 一、Rabbit 二、篱笆墙的影子 三、丢失的MD5 四、Alice与Bob 一、Rabbit 得到的密文&#xff1a;U2FsdGVkX1/ydnDPowGbjjJXhZxm2MP2AgI 依旧是看不懂是什么编码&#xff0c;上网搜索&#xff0c;在侧栏发现Rabbit解码&#xff0c;直接搜索就能有在线解码网站 二、篱笆…

【Vue第3章】使用Vue脚手架_Vue2

目录 3.1 初始化脚手架 3.1.1 说明 3.1.2 具体步骤 3.1.3 模板项目的结构 3.1.4 笔记与代码 3.1.4.1 笔记 3.1.4.2 01_src_分析脚手架 3.2 ref与props 3.2.1 ref 3.2.2 props 3.2.3 笔记与代码 3.2.3.1 笔记 3.2.3.2 02_src_ref属性 3.2.3.3 03_src_props配置 3…

【Linux】:线程(二)互斥

互斥与同步 一.线程的局部存储二.线程的分离三.互斥1.一些概念2.上锁3.锁的原理4.死锁 一.线程的局部存储 例子 可以看到全局变量是所有线程共享的&#xff0c;如果我们想要每个线程都单独访问g_val怎么办呢&#xff1f;其实我们可以在它前面加上__thread修饰。 这就相当于把g…

Java面向对象实践小结(含面试题)

继承 作用 提高了代码的复用性。让类与类之间产生了关系。有了这个关系&#xff0c;才有了多态的特性。 代码示范 父类代码 public class Parent {public void say() {System.out.println("父类的say方法");} }子类代码&#xff0c;继承父类&#xff0c;也就拥有…

如何确认网站是否有漏洞,如何找出网站存在的漏洞,找到漏洞该如何处理

如何确认网站或者服务器是否有漏洞 判断一个网站是否是存在漏洞的方法&#xff1a; 1.可以借助德迅云安全漏洞扫描功能来检查漏洞。 2.打开德迅云安全首页&#xff0c;点击最上面导航栏中的“安全产品”。 3.滑到“漏洞扫描”&#xff0c;选择“产品价格”服务。 4.选择您需…

【图像分类】【深度学习】【Pytorch版本】 DenseNet模型算法详解

【图像分类】【深度学习】【Pytorch版本】 DenseNet模型算法详解 文章目录 【图像分类】【深度学习】【Pytorch版本】 DenseNet模型算法详解前言DenseNet讲解Dense Block(稠密块)Dense Layer(稠密层)Transition Layer 过渡层DenseNet模型结构 DenseNet Pytorch代码完整代码附加…

DM8/达梦 数据库管理员使用手册详解

1.1DM客户端存放位置 Windows&#xff1a;DM数据库安装目录中tool文件夹和bin文件夹中。 Linux&#xff1a;DM数据库安装目录中tool目录和bin目录中。 1.2DM数据库配置助手 1.2.1Windows创建数据库 打开数据库配置助手dbca 点击创建数据库实例 选择一般用途 浏览选择数据库…

【基于大数据的人肥胖程度预测分析与可控策略】

基于大数据的人肥胖程度预测分析与可控策略 前言数据获取与清洗数据挖掘与分类建模1. K-means聚类2. 层次聚类3. DBSCAN4. 分类建模 数据可视化模型肥胖程度预测分析与可控策略结语 前言 随着现代生活方式的改变&#xff0c;肥胖问题逐渐成为全球性的健康挑战。为了更好地理解…

湖科大计网:计算机网络概述

一、计算机网络的性能指标 一、速率 有时候数据量也认为是以10为底的&#xff0c;看怎么好算。&#xff08;具体吉大考试用什么待商榷&#xff09; 二、带宽 在模拟信号系统中带宽的含义&#xff0c;本课程中用到的地方是&#xff1a;香农定理和奈奎斯特定理公式的应用之中。 …

未用的引脚如何处理?--持续更新中

前言&#xff1a; 随着集成电路规模的越来越大&#xff0c;如今的大规模芯片都集成了很多功能模块&#xff0c;但是在实际的电路设计中我们又不可能把芯片所有的功能模块(或者说接口)全部用上&#xff0c;因此总会有或多或少的管脚会“用不上”&#xff0c;那这些未用的管脚一般…

maven上传jar包到代码仓库

一、前言 一般被引用的包开发都是要求放在nexus仓库中&#xff0c;等到有jar包服务需要引用该包的时候直接从nexus仓库中获取即可&#xff0c;实现了该引用包的公用 二、代码配置 编辑代码中的pom.xml文件配置 vi pom.xml <distributionManagement><repository>&…

网络安全行业大模型调研总结

随着人工智能技术的发展&#xff0c;安全行业大模型SecLLM&#xff08;security Large Language Model&#xff09;应运而生&#xff0c;可应用于代码漏洞挖掘、安全智能问答、多源情报整合、勒索情报挖掘、安全评估、安全事件研判等场景。 本文首先介绍汇总了安全行业的大模型…

Java、JDK、JRE、JVM

Java、JDK、JRE、JVM 一、 Java 广义上看&#xff0c;Kotlin、JRuby等运行于Java虚拟机上的编程语言以及相关的程序都属于Java体系的一员。从传统意义上看&#xff0c;Java社区规定的Java技术体系包括以下几个部分&#xff1a; Java程序设计语言各种硬件平台上的Java虚拟机实…

Linux 进程终止

引入 在写 C 语言程序的时候&#xff0c;我们必写的结构就是&#xff1a; int main() {return 0; }在学习 C 语言的时候&#xff0c;我们好像并没有讨论过这个 return 0 有什么用&#xff0c;是干什么的&#xff01;return 1 可以吗&#xff1f;return 的返回值给谁看&#x…

英伟达危机大爆发!一夜之间,四面楚歌

今年以来&#xff0c;AI大模型明争暗斗、百花齐放。 但不管各种大模型打的有多厉害&#xff0c;很多人都认为“卖铲子”的英伟达才是最大赢家。 看一下英伟达今年的股票就知道英伟达赚的是多么盆满钵满。 英伟达CEO黄仁勋在发布 H200显卡时&#xff0c;应该是今年最意气风发的…