整洁架构在前端的设计思想与应用实践

这篇具有很好参考价值的文章主要介绍了整洁架构在前端的设计思想与应用实践。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

整洁架构在前端的设计思想与应用实践

随着业务的发展,前端项目承载了越来越多的职责,也越来越复杂,简单通过 cli 生成的框架结构越来越无法满足。
面对前端项目复杂度的不断提升,我们开始思考前端的架构组织方式怎么才更合理?
应该如何设计良好的前端架构?
行业是否有比较好的优秀实践?
本文先从架构基本概念开始介绍,然后介绍整洁架构的概念和设计理念,最后结合整洁架构、 DDD 方法论,一起探讨整洁架构在前端的应用实践。

1、 为什么需要了解架构

对于每个软件系统,我们都可以通过行为和架构两个维度来体现它的实际价值。

行为是指系统实现的功能特性,一般是比较紧急的,需要按时上线。架构就是指系统架构,是重要的,但是并不总是特别紧急。因此导致我们常常忽视系统的架构价值,使得系统越来越难于理解、修改,导致系统功能迭代成本逐步上升,生产力逐步下降。

整洁架构在前端的设计思想与应用实践

如果你遇到了这个问题,就应该要了解架构了,思考当前系统架构是否合理。

那什么是架构呢?

架构的本质就是控制系统复杂度,其终极目标用最小的人力成本来满足构建和维护系统需求,同时最小化系统的总运营成本,确保系统不会因为增加功能而导致开发成本上升。

那如何来判断架构的优劣?

一个软件架构的优劣,可以用它满足用户需求所需要的成本来衡量。如果该成本很低,系统理解成本低、易于修改、方便维护,能轻松部署,并且在系统的整个生命周期内一直都能维持这样的低成本,那么这个系统的设计就是优良的。反之,如果该系统的每次发布都会提升下一次变更的成本,那么这个设计就是不好的。

这样的架构可以大大节省软件项目构建与维护的人力成本。让每次变更都短小简单,易于实施,并且避免缺陷,用最小的成本,最大程度地满足功能性和灵活性的要求。

那么良好的架构是怎么实现的呢?

良好的架构实现方式,一般都是通过模块化解耦、分层解耦,实现关注点分离,并通过一定的规则组织好不同模块、不同分层的关系,实现高内聚低耦合,从而控制系统的复杂度。

整洁架构就是其中一种经典架构,让你我不再为每次功能迭代而胆战心惊,那么接下来我们将介绍何为『整洁架构』,为什么说它是一个好的软件架构。

 

2、 整洁架构的定义

整洁架构(Clean Architecture)是一种软件架构设计原则,由罗伯特·C·马丁(Robert C. Martin)提出,它旨在使软件系统更加灵活、可维护和可测试。

主要特点如下:

  • 与框架无关: 无论是前端代码还是服务端代码,其逻辑本身都应该是独立的,不应该依赖于某一个第三方框架或工具库。比如不依赖 Vue.js、React 等框架。
  • 可测试性:代码中的业务逻辑可以在不依赖 ui、数据库、服务器的情况下进行测试。
  • 和 ui 无关:代码中的业务逻辑不应该和 ui 做强绑定。比如把一个 web 应用切换成桌面应用,业务逻辑不应该受到影响。
  • 和数据库无关:无论数据库用的是 mysql 还是 mongodb,无论其怎么变,都不该影响到业务逻辑。
  • 和外部服务无关:将业务逻辑置于系统的核心,无论外部服务怎么变,都不影响到使用该服务的业务逻辑。

一个优秀的软件架构师应该致力于最大化架构组件的可选项数量,可以低成本更换框架、数据库、外部服务等,接下来我们具体看下整洁架构的设计思想。

 

3、 整洁架构的设计

3.1、整洁架构的设计思想

整洁架构在前端的设计思想与应用实践

整洁架构除了以下至少四层架构外,在层与层之间还有一个非常明确的依赖关系,外层的逻辑依赖内层的逻辑 (图中黑色箭头指向),但是内层的代码不可以依赖外层。

  • 实体层: 业务实体这一层中封装的是整个系统的关键业务逻辑。这些实体既可以是带方法的类,也可以是带有一堆函数的结构体。但它们必须是高度抽象的,封装了该应用中最通用、最高层的业务逻辑,只可以随着核心业务规则变化,不可以随着外层组件的变化而变化。例如,一个针对页面导航方式或者安全问题的修改不应该触及这些对象,一个针对应用在运行时的行为所做的变更也不应该影响业务实体。该层一般采用 DDD 的理念进行抽象、封装。
  • 用例层: 软件的用例层中通常包含的是特定应用场景下的业务逻辑,这里面封装并实现了整个系统的所有用例。该层控制所有流向和流出实体层的数据流,并使用核心的实体及其业务规则来完成业务需求。此层的变更不会影响实体层,更外层的变更,比如开发框架、数据库、UI 等变化,也不会影响此层。
  • 适配器层: 软件的接口适配器层中通常是一组数据转换器,它们负责将数据从对用例和业务实体而言最方便操作的格式,转化成外部系统(譬如数据库以及 Web)最方便操作的格式。反之,来自于外部服务的数据也会在这层转换为内层需要的结构,一般用于 ui 和接口的适配操作。
  • 框架和驱动层:由最外层由各种框架和工具组成,比如 Web 框架、数据库访问工具等。通常在这层不需要写太多代码,大多是一些用来跟内层通信的胶水代码。

了解了整洁结构的设计思想,那么它和其他经典的架构有什么区别呢?

3.2、整洁架构和其他架构对比

我们先了解下最常见的六边形架构和 DDD 分层架构。

3.2.1、六边形架构
整洁架构在前端的设计思想与应用实践
本图片来源《DDD 实战课》

其核心理念是:应用是通过端口与外部进行交互的 。也就是说,在上图的六边形架构中,红圈内的核心业务逻辑(应用程序和领域模型)与外部资源(包括 APP、Web 应用以及数据库资源等)完全隔离,仅通过适配器进行交互。

它解决了业务逻辑与用户界面的代码交错问题,很好地实现了前后端分离。

3.2.2、DDD 分层架构
整洁架构在前端的设计思想与应用实践

核心理念:领域驱动设计。领域模型准确反映了业务语言,而传统数据对象除了简单 setter/getter 方法外,没有任何业务方法

  • 用户界面层:负责向用户显示信息和解释用户指令,也是我们常说的 UI 层
  • 应用层:负责使用领域层提供的能力进行业务流程编排,实现对应功能
  • 领域层:领域模型/领域服务/和防腐层的接口定义,为应用层提供能力
  • 基础设施层:为其它各层提供通用的技术和基础服务,如数据持久化、消息中间件等

具体实践是一般第一步为自上而下结构化分解,如图:

整洁架构在前端的设计思想与应用实践

图片来自于张建飞《基于 DDD 的应用架构设计和实践》分享

第二步为自下而上的领域建模,从而完成功能的实现,如图

整洁架构在前端的设计思想与应用实践

图片来自于张建飞《基于 DDD 的应用架构设计和实践》分享

总结起来就是先把业务逻辑按结构化拆解,拆解为不同的步骤,然后调用领域层的能力进行逻辑编排实现对应功能。

整洁架构在前端的设计思想与应用实践

图片来自于张建飞《基于 DDD 的应用架构设计和实践》分享

3.2.3、对比分析
整洁架构在前端的设计思想与应用实践

本图片来源《DDD 实战课》

可以看到他们的共同点是:整洁架构、DDD 分层架构、六边形架构都是以领域模型为核心,实行分层架构,内部核心业务逻辑与外部应用、资源隔离并解耦。

事实上整洁架构恰恰是最后的集大成者,集合了 DDD 领域驱动的思想 + 分层架构的落地,具体可以如下架构发展历史图。

整洁架构在前端的设计思想与应用实践

图片来源于《领域驱动架构及其演变史(EBI、DDD、端口适配、洋葱、整洁)》

了解了整洁架构的优势,接下来我们重点介绍如何应用整洁架构

 

4、 如何应用整洁架构?

首先会借鉴 DDD 的思想进行业务分析、建模,形成业务的领域模型。

4.1 战略阶段:分析业务,建立领域模型

4.1.1 分析业务流程

DDD 中一般采用用例分析、事件风暴、四色建模等方法,尽可能全面不遗漏的分解业务领域,梳理业务过程中的用户操作、事件以及依赖关系,再根据这些要素进一步梳理出领域对象及他们之间的关系。

DDD 里说的这些业务分析方法在构建大型项目时非常有用,但在日常需求中会显得有点重。于是我们结合用例分析与事件风暴,沉淀出一套适合日常需求分析的方法论,内部称之为双轴泳道分析法。

下面以电商购物需求为例,介绍一下实施步骤:

  • 识别业务参与者

业务参与者,是指在业务流程中发起动作,触发状态改变的个体。业务参与者可以某个角色、某个系统、或者某个综合系统。

例如电商网站购物场景中,用户选品、下单、支付,电商网页负责呈现商品信息,提示用户操作结果,电商后台负责生成订单、记账。用户(角色)、电商网页(系统)、电商后台(系统)都是业务参与者,其概念类似用例分析里的 actor。

  • 分析参与者在不同阶段发生的动作及触发的状态

动作是指参与者发起的某个命令,比如创建订单、抽奖等,而状态是指动作发生后引起的状态变更,比如订单已创建,订单创建失败等,其概念类似事件风暴的命令和事件。

对电商购物场景分析结果如下:

整洁架构在前端的设计思想与应用实践
  • 使用双轴泳道图描述业务流程

泳道的横轴是业务参与者,纵轴是业务流程的不同阶段,通过双轴泳道图描述出各个参与者在不同阶段发生的动作、触发的状态。

在业务流程中,有些属于前端交互,有些属于动作,有些属于状态。动作和状态会用于后续的领域对象提取,我们需要将他们标注出来以便识别。

整洁架构在前端的设计思想与应用实践
4.1.2 提取领域对象

经过上述分析后,业务流程已经非常清晰。第二步,就是要根据分析过程中产生的动作和状态,提取出产生这些行为的对象,进一步识别出实体、值对象、聚合根。

  • 实体

业务形态上是包含业务规则的集合,具有唯一标识字段(id)。代码上通常以类/对象的形式存在,包含属性和方法。

  • 值对象

业务形态上是干个属性的集合,只有数据初始化操作和有限的不涉及修改数据的行为,不具有唯一标识(id)。代码上以类/对象的形式被实体引用。

  • 聚合根

聚合根是一个特殊实体,具备唯一标识(id),有独立的生命周期。聚合根是聚合的唯一入口点,负责协调实体以及值对象完成业务逻辑。

整洁架构在前端的设计思想与应用实践

在以上例子中,将动作、状态归类后,可划分为用户、购物车、商品、订单、消费流水五个实体,收货地址可作为值对象。

4.1.3 划分界限,识别模块

根据上下文语义,寻找聚合根、划定界限,将实体进一步组合成聚合,一个聚合对应一个模块。

在本例子中:

  • 用户实体和购物车实体与用户强相关,分别管理用户基本信息和购物车信息,可以用户实体为聚合根,共同构成用户模块;
  • 订单实体、消费流水实体与下单强相关,可以订单实体为聚合更,共同构成下单模块;
  • 商品模块较独立,可单独构成商品模块。
整洁架构在前端的设计思想与应用实践

4.2 战术阶段:工程落地,搭建分层架构

通过战略阶段建立对应的领域模型后,在对应的工程实现上,应如何划分层呢?

4.2.1 分层实现

以前端工程为例,常规的 mvvm 前端工程的分层架构如下图,会在 store 层直接调用 api 层发起请求,然后再通过 mvvm 更新视图

整洁架构在前端的设计思想与应用实践

❌ 容易出现的问题:

  • 业务逻辑和 ui 强耦合,如果要更换 ui,改动成本大
  • 往往 store 层依赖框架的实现,业务逻辑易和框架强耦合,如果切换框架或升级框架,重构成本大。比如升级 vue 到 vue2,或 vue 切换到 react。

根据整洁架构思想,设计后的架构如下:

整洁架构在前端的设计思想与应用实践

在原有基础上拆分了实体层和用例层,并在用例层内通过端口的方式定义了依赖的端口方法,用来解耦框架和第三方服务的依赖。

目前很多前端实践里实体层是比较薄,有的只有类型定义,把逻辑封装到了用例层,但用例层的逻辑不适合更细粒度的复用,导致复用比较麻烦,这也不符合整洁架构对实体层的定义,整洁架构中期望实体这一层中封装的是整个系统的关键业务逻辑。

个人觉得应该视具体情况而定,逻辑简单的前端页面,用例层和实体层都比较简单,可以使用贫血的实体层;如果逻辑复杂的一定要把逻辑抽取到实体层,用例层使用实体层提供的能力进行功能串联,方便复用及后续维护。比如我们这边的下载逻辑就比较重,需要把相关逻辑封装在实体层。

比如购买这个用例里,需要判断是否登录,判断是否有库存,创建订单,支付等流程,每个流程应该使用的都是实体的能力,具体的逻辑封装在实体里,用例层核心是实现流程的串联。

整洁架构在前端的设计思想与应用实践
img

下面我们看下这样分层后的代码示例及数据流向是怎么样的?

以最常见的电商商品展示为示例,用户登录后查看商品详情,根据用户所在地展示商品库存。整个流程是这样的:进到页面 -> 检查登录态 -> 发起请求 -> 组装数据 -> 页面展示

实体层

关于实体层的设计有两个要点:

  • 使用充血模型来描述实体

充血模型指的是,实体内包含数据及常用行为,符合面向对象的封装性,是典型的面向对象编程风格。反之贫血模型指的是实体只包含数据,行为不封装在实体内,是一种面向过程的设计。

  • 结合具体场景,允许部分依赖实现

众所周知,DDD 中非常强调领域层的解耦,理论上领域层应该依赖抽象接口,不应该依赖具体实现。这种彻底解耦的方式的确能解决后续依赖项变更的问题,但在实际开发中,很多依赖项是我们可控的,可预知后续是不会变更的,这种情况下如果对所有依赖都要抽象出接口,那将会大大增加我们的工作量。因此我们提倡结合具体的场景,只对后续可能变化的依赖进行防腐,对于后续不会变化的依赖我们允许直接依赖实现。

本例子中,可拆分成用户、商品两个实体。

用户实体,主要提供用户常用的登录、登出、查询用户所在城市等方法。用户的登录态一般依赖 cookie,浏览器的 cookie 接口不大可能出现破坏性变更,因此在用户实体中,我们允许直接依赖 cookie 操作库,而查询用户城市依赖于用户服务提供接口,为防止后端接口变更,需要对用户服务进行防腐。

// 用户实体 ./shared/domain/entities/user.ts

import cookie from 'cookie';

export interface IUserService {
    getCity(id: string): Promise<City>;
}

export class User {
    // 用户Id
    public id: string;
    // 用户服务
    private userService: IUserService;

    constructor(id: string, name: string, userService: IUserService) {}

    // 检查用户是否登录
    public isLogin(): boolean {
        if (cookie.get('openid') && cookie.get('access_token')) {
            return true;
        }

        return false;
    }

    // 登录
    public login(): Promise<void> {
        if (!this.isLogin()) {
            goToURL('https://www.xxx.com/login');
        }
    }

    // 退出登录
    public logout(): Promise<void> {
        cookie.remove('openid');
        cookie.remove('access_token');
        goToURL('https://www.xxx.com/login');
    }

    // 获取用户所在城市
    public getCity(): Promise<City> {
        return this.userService.getCity(this.id);
    }
}

商品实体:提供查询商品详情方法,商品实体依赖后端的商品服务,为防止后端接口变更,需要进行防腐:

// 商品实体 ./shared/domain/entities/product.ts

export interface IProductService {
    getBaseInfoById(id: string): Promise<ProductBaseInfo>;
    getStockInfoByIdAndCity(id: string, city: City): Promise<ProductStockInfo>;
}

export class Product {
  // 商品Id
  public id: string;
  // 用户服务
  private productService: ProductService;

  constructor(id: string, name: string; productService: IProductService) {}

  // 获取商品详情
  public async getDetail() {
      // 获取商品基本信息和库存信息
      const baseInfo = await this.productService.getBaseInfoById(this.id);
      const stockInfo = await this.productService.getStockInfoById(this.id, city);
      // 组合详情数据
      const detail = {
          id: this.id,
          name: baseinfo.name,
          images: baseinfo.name,
          stockNum: stockInfo.num,
      };
      return detail;
  }

  // 根据地区获取库存信息
  public addToCart(num:number) {
      return this.productService.getStockInfoById(this.id, city);
  }
};

用例层

用例层主要充当“协调者”的角色,组合各个实体的操作,实现业务逻辑,这层的逻辑代码会“面向过程”。

本例子中,需要结合用户实体和商品实体,实现根据用户所在地获取商品库存信息

// 获取商品详情用例 ./shared/domain/usercases/get-product-detail.ts

import { User } from './shared/domain/entities/user.ts';
import { Product } from './shared/domain/entities/product.ts';
// 用户服务、产品服务的具体实现,见适配器层
import { UserService } from './server/services/user-service.ts';
import { ProductService } from './server/services/product-service.ts';

export async function getProductDetail(userId: string, productId: string) {
    // 示例化用户实体和商品实体,省略部分代码
    const user = new User(userId, UserService);
    const product = new Product(productId, ProductService);

    // 获取用户所在城市
    const city: City = await user.getCity();
    // 获取商品基本信息
    const productBaseInfo = await product.getBaseInfo();
    // 根据城市获取商品库存
    const productStockInfo = await product.getStockInfo(city);
    return {
        baseInfo: productBaseInfo,
        stockInfo: productStockInfo,
    };
}

适配器层

  • 包含 UI 框架的代码,及 store 相关的代码,如 vuex,通过更新 vuex 的数据更新视图
  • 调用第三方服务,并将其转化成用例层的端口格式
// 用户服务具体实现 ./server/services/user-service.ts
import { IUserService } from './shared/domain/entities/user.ts';

class UserService implements IUserService {
    getCity(userId: string): Promise<City> {
        // 通过后台接口获取用户所在城市
        const resp = get('https://api.xxx.com/queryUserCity', { userId });
        if (resp.ret !== 0) {
            throw new Error('查询用户所在城市失败');
        }

        return resp.data.city as City;
    }

}
// 商品服务具体实现 ./server/services/product-service.ts
import { IProductService } from './shared/domain/entities/product.ts';

class ProductService implements IProductService {
    getBaseInfoById(id: string): Promise<ProductBaseInfo> {
        // 调用后台商品服务接口,省略具体实现
    }
    getStockInfoByIdAndCity(id: string, city: City): Promise<ProductStockInfo> {
        // 调用后台商品服务接口,省略具体实现
    }
}
// 商品详情页 store ./client/store/product-store.ts
import { getProductDetial } from './shared/domain/usercases/get-product-detail.ts'

export default new Vuex.Store({
  state: {
    productDetail: ProductDetail,
  },
  mutations: {
    async getProductDetail(state) {
        // 用例已包含具体业务逻辑,这里直接调用用例方法
        state.productDetail = getProductDetial(userId, productId);
    },
  },
}
// 商品详情页 ./client/pages/product-detail.ts

import { defineComponent, ref, onMounted } from 'vue';

export defineComponent({
  name: 'ProudctDetailPage',
  setup() {

     onMounted(() => {
         setLoading(true);
         await store.getProductDetail();
         setLoading(false);
      });

    return () => (
      <div>
        <p> {{ store.productDetail.baseInfo }}</p>
        <p> {{ store.productDetail.stockInfo }}</p>
      </div>
    );
  },
});

框架和驱动层

  • 这里是用到的第三方服务、框架,如 vue、svelte 等

如以下伪代码

import vue from 'Vue'

vue.render(App);

整体数据流向图如下

4.2.2 依据 SOLID 原则实现分层
  • S 单一职责原则
  • O 开闭原则
  • L 里氏替换原则
  • I 接口隔离原则
  • D 依赖倒置原则

PS: 😁 由于网上对 SOLID 原则有较多介绍,这里就不额外展开了,有兴趣的同学可查阅学习

4.3.3 架构及目录示例

基于此,我们采用整洁架构后目录结构如下,如下图所示

img
  • 单独抽离领域层(包括实体层、用例层、防腐层)目录
  • 将 utils 工具进行拆解,无"副作用" 的 utils 移动至 shared 目录下,界面相关如 jump 存放到对应的 client 的 utils 下

5、总结

整洁架构不是"银弹",在实践上存在以下优缺点:

✅ 优点:

  • 业务领域层逻辑更干净,业务逻辑可适配到不同的 UI 框架、对于同构的 SSR 服务也可以公用同一套业务逻辑
  • 职责边界更为明确,内层的业务逻辑可覆盖单元测试,ui 层则依赖 e2e 端对端测试覆盖

❌ 缺点:

  • 构建边界的成本较大,由于核心业务层无法直接引用外层 UI 的 store 和 api,需额外声明端口依赖,开发效率变低

所以说没有最好的架构,只有最适合自己团队和业务的架构。对于是否使用整洁架构,我们应考量项目复杂度、项目的生命周期,综合来衡量。对于业务逻辑简单、业务生命周期较短的项目,直接使用照搬整洁架构,会导致开发效率地变低;但是对于需要长期维护的复杂项目,如腾讯文档、vsCode 内核、低代码引擎等,就非常适合整洁架构,能大大降低系统的维护成本,并在前端技术快速变迁的情况下,非常方便后续对 UI 库、框架的升级改造。

期望大家在日常工作中除了关注系统的行为,多一些对架构的关注和思考,以提升系统的整洁性,让每次变更都短小简单,易于实施,并且避免缺陷,用最小的成本,最大程度地满足功能性和灵活性的要求。

最后感谢您的阅读,如果本文给你带来了一些启发,欢迎动动手指,一键三连,这是对笔者最大的支持和鼓励。

 

6、 参考文献

《整洁架构之道》书籍

《基于 DDD 的应用架构设计和实践》视频分享

DDD 实战课

领域驱动架构及其演变史(EBI、DDD、端口适配、洋葱、整洁)

 

作者:owen、avery、challen 等文章来源地址https://www.toymoban.com/news/detail-710174.html

到了这里,关于整洁架构在前端的设计思想与应用实践的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 快速理解DDD领域驱动设计架构思想-基础篇 | 京东物流技术团队

    本文与大家一起学习并介绍领域驱动设计(Domain Drive Design) 简称DDD,以及为什么我们需要领域驱动设计,它有哪些优缺点,尽量用一些通俗易懂文字来描述讲解领域驱动设计,本篇并不会从深层大论述讲解落地实现,这些大家可以在了解入门后再去深层次学习探讨或在后续进阶

    2024年02月09日
    浏览(42)
  • 《移动互联网技术》 第十章 系统与通信: 掌握Android系统的分层架构设计思想和基于组件的设计模式

    🌷🍁 博主 libin9iOak带您 Go to New World.✨🍁 🦄 个人主页——libin9iOak的博客🎐 🐳 《面试题大全》 文章图文并茂🦕生动形象🦖简单易学!欢迎大家来踩踩~🌺 🌊 《IDEA开发秘籍》学会IDEA常用操作,工作效率翻倍~💐 🪁🍁 希望本文能够给您带来一定的帮助🌸文章粗浅,敬

    2024年02月16日
    浏览(54)
  • 设计模式-01.设计思想

    此系列文章非本人原创,是学习笔记。 下面讲一些常见的设计思想 这个原则非常重要,是一种非常有效的提高代码质量的手段,在平时的开发中特别经常被用到。 如何解读原则中的“接口”二字? “基于接口而非实现编程”这条原则的英文描述是:“Program to an interface, n

    2024年02月07日
    浏览(69)
  • Spring核心设计思想

    目录 前言: Spring是什么 什么是IoC 传统开发思想 IoC开发思想 Spring IoC 什么是DI 小结:     官网中提出:Spring makes programming Java quicker, easier, and safer for everybody. Spring’s focus on speed, simplicity, and productivity has made it the world\\\'s most popular Java framework.     Spring 使编程 Java 对每个人来

    2023年04月17日
    浏览(42)
  • 算法设计思想——动态规划

    是一种常见的算法设计方法,用于解决一类重叠子问题的优化问题。他的基本思想是将问题分解成多个重叠的子问题,递归求解,并将子问题的求解缓存起来,避免重复计算,从而得到问题的解。 动态规划通常适用于以下两个条件的问题: 1.重叠子问题:原问题可以分解为若

    2024年02月03日
    浏览(45)
  • 闪电网络协议设计思想剖析

    闪电网络可能是比特币之上部署的最受期待的技术创新。闪电网络,为由 Joseph Poon 和 Tadge Dryja 于2015年首次提出的支付层,承诺支持: 用户之间几乎无限数量的链下交易, 几乎免费, 同时利用比特币提供的安全性。 2016年时,至少三个公司——Poon 和 Dryja 的 Lightning、 Block

    2024年03月20日
    浏览(61)
  • Spring 核心与设计思想

    ✏️作者:银河罐头 📋系列专栏:JavaEE 🌲 “种一棵树最好的时间是十年前,其次是现在” 通常所说的 Spring 指的是 Spring Framework(Spring 框架)。 Spring 是包含多种工具方法的 IoC 容器。 IoC(Inversion of Control): 控制反转 \\\"控制反转\\\"又是什么意思? 下面以一个程序来举例。 假如我

    2024年02月02日
    浏览(55)
  • 【Spring】核心与设计思想

     哈喽,哈喽,大家好~ 我是你们的老朋友: 保护小周ღ   谈起Java 圈子里的框架,最年长最耀眼的莫过于 Spring 框架啦,如今已成为最流行、最广泛使用的Java开发框架之一。不知道大家有没有在使用 Spring 框架的时候思考过这些问题, 什么是框架?Spring 是什么?如何理解

    2024年02月08日
    浏览(43)
  • Spring框架核心与设计思想

    我们一般所说的Spring指的是Spring Framework(Spring 框架),它是一个开源的框架,Spring支持广泛的应用场景,它可以让Java企业级的应用程序开发变得更简单,官方一点的回答:spring是J2EE应用程序框架,是轻量级的IoC和AOP的容器框架,主要是针对javaBean的生命周期进行管理的轻量级

    2023年04月15日
    浏览(44)
  • Spring框架概述及核心设计思想

    我们通常所说的 Spring 指的是 Spring Framework(Spring 框架),它是⼀个开源框架,有着活跃而庞大的社区,这就是它之所以能长久不衰的原因;Spring 支持广泛的应用场景,它可以让 Java 企业级的应用程序开发起来更简单。 用⼀句话概括 Spring: Spring 框架是包含了众多工具方法的

    2024年02月16日
    浏览(37)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包