当前位置: 首页 > news >正文

敏捷开发笔记(第9章节)--开放-封闭原则(OCP)

目录

1:PDF上传链接

9.1 开放-封闭原则(OCP)

9.2 描述

9.3 关键是抽象

9.3.1 shape应用程序

9.3.2 违反OCP

糟糕的设计

9.3.3 遵循OCP

9.3.4 是的,我说谎了

9.3.5 预测变化和“贴切的”结构

9.3.6 放置吊钩

1.只受一次愚弄

2.刺激变化

9.3.7 使用抽象获得显示封闭

9.3.8 使用“数据驱动”的方法获取封闭性

9.4 结论


1:PDF上传链接

【免费】敏捷软件开发(原则模式与实践)资源-CSDN文库

        Ivar Jacobson曾说过,“任何系统在其生命周期中都会发生变化。如果我们期望开发出的系统不会再第一版后就被抛弃你就必须牢牢记住一点”。Bertrand Meyer在1988年提出著名的开发-封闭原则(The Open - closed Principle,简称OCP)为我们提供了指引。

9.1 开放-封闭原则(OCP)

        软件实体(类、模块、函数等等)应该是可以扩展的,但是不可修改的。

        如果程序中的一处改动就会产生连锁反应,导致一系列相关模块的改动,那么设计就具有僵化性的臭味。OCP建议我们应该对系统进行重构,这样以后对系统在进行那样的改动时,就不会导致更多的修改。如果正确地应用OCP,那么以后再进行同样的改动,就只需要添加新的代码,而不必改动已经正常运行的代码。

        也许,这看起来像是重所周知的可望而不可及的美好理想-----然后,事实上却有一些相对简单并且有效的策略可以帮助接近这个理想。

9.2 描述

        遵循开放-封闭原则设计出的模块具有两个主要的特征。它们是:

        1:对于扩展是开放的(Open for extension)

                这意味着模块的行为是可以扩展的,当应用的需求改变时,我们可以对模块进行扩展,使其具有满足那些改变的新行为,换句话说,我们可以改变模块的功能。

        2:对更改是封闭的(closed for modification)

                对模块行为进行扩展时,不必改动模块的源代码或者二进制代码。模块的二进制可执行版本,无论是可链接的库、dll或者Java的jar文件都无需改动。

        这个两个特征好像是相互矛盾的,扩展模块行为的通常方式就是修改该模块的源代码。不允许修改的模块常常都被认为具有固定的行为。

        怎么可能在不改动模块源代码的情况下,去更改它的行为呢?怎么才能在无需对模块进行改动情况下就改变它的功能呢?

9.3 关键是抽象

        在C++、Java或者其他任何的OOPL(面向对象编程语言)中,可以创建出固定去能够描述一组任意个可能行为的抽象体。这个抽象体就是抽象类。而这一组任意个可能得行为则表现为可能得派生类。

        模块可以操作一个抽象体。由于模块依赖于一个固定的抽象体,所以他对于更改可以是关闭的,同时,通过从这个抽象体派生,也可以扩展此模块的行为。

       

9.3.1 shape应用程序

        下面的例子在许多讲述OOD(面向对象的设计)的书中都提过。他就是声名狼藉的“shape”样列。它常常被用来展示多态的工作原理。不过,这次我们将使用它来阐明OCP。

        我们有一个需要再标准的GUI上面绘制圆和正方形的应用程序。圆和正方形必须要按照特定的顺序绘制。我们将创建一个列表,列表由按照适当的顺序排列的圆和正方形组成,程序遍历该列表,依次绘制出每个圆和正方形。

9.3.2 违反OCP

        如果使用C语言,并采用不遵循OCP的过程化方法,我们也许会得到程序9.1中所示的解决方法。其中,我们看到了一组的数据结构,它们的第一个成员都相同,但是其余的成员都不同。每个结构中的第一个成员都是一个用来标识该结构是代表圆或者正方形的类型码。DrawAllShapes函数遍历一个数组,该数组的元素是指向这些数据结构的指针,DrawAllShapes函数先检查类型码,然后根据类型码调用对应的函数(DrawCircle或者DrawSquare)。

程序9.1 Square/Circle问题的过程化解决方案
--shape.h --
enum ShapeType {circle,square
};    
struct Shape {ShapeType itsType;
}
-circle.h ---
struct Circle {ShapeType itsType;double itsRadius;Point itsCenter;
}
-square.h ----
struct Square {ShapeType itsType;double itsside;Point itsTopLeft;
}:
--drawA11 Shapes.cc----------------
typedef struct Shape *Shapepointer;void DrawAllShapes(ShapePointer list[], int n)
{int i;for (i = 0; i < n; i++){struct Shape *s = list[i];switch (s->itsType) {case square:DrawSquare((struct Square*)s);break;case circle:Drawcircle((struct circle*)s);Break;default:break;}}
}

        DrawAllShapes函数不符合OCP,因为它对于新的形状类型的添加不是封闭的。如果希望这个函数能够绘制包含有三角形的列表,就必须得更改这个函数。事实上,每增加一种新的形状类型,都必须要更改这个函数。
        当然这只是一个简单的例子。在实际程序中,类似DrawAllShapes函数中的switch语句会在应用程序的各个函数中重复不断地出现,每个函数中switch语句负责完成的工作差别甚微。这些函数中,可能有负责拖曳形状对象的,有负责拉伸形状对象的,有负责移动形状对象的,有负责删除形状对象的,等等。在这样的应用程序中增加一种新的形状类型,就意味着要找出所有包含上述switch语句(或者链式if/else语句)的函数,并在每一处都添加对新增的形状类型的判断。
        更糟的是,并不是所有的switch语句和if/else链都像DrawAllShapes中的那样有比较好的结构。更有可能的情形是,if语句中的判断条件由逻辑操作符组合而成,或者是处理方式相同的case语句被成组处理。在一些极端错误的实现中,会有一些函数对于Square的处理竞然和对于circle的处理一样。在这样的函数中,甚至根本就没有switch/case语句或者if/else链。这样,要发现和理解所有的需要增加对新的形状类型进行判断的地方,恐怕就非常的困难了。
        同样,在进行上述改动时,我们必须要在ShapeType enum中添加·个新的成员。由于所有不同种类的形状都依赖于这个eum的声明,所以我们必须要重新编译所有的形状模块。并且也必须要重新编译所有依赖于Shape类的模块。

糟糕的设计

        再来回顾一下。程序9.1中的解决方法是僵化的,这是因为增加Triangle会导致Shape、Square、Circle以及DrawAllShapes的重新编译和重新部署。该方法是脆弱的,因为有许多其他的即难以查找又难以理解的switch/case或者lse语句。该方法是牢固的,因为想在另一个程序中复用DrawAllShapes时,都必须要附带上Square和Circle,即使那个新程序不需要它们。因此,在程序9.1中展示了许多糟糕设计的臭味。

9.3.3 遵循OCP

        程序9.2中展示了一个square/circle问题的符合OCP的解决方案。在这个方案中,我们编写了一个名为Shape的抽象类。这个抽象类仅有一个名为Draw的抽象方法。Circle和Square都从Shape类派生。

程序9.2问题的OoD解决方案class Shape {public:virtual void Draw () const = 0;
};class Square: public Shape {public:virtual void Draw() const = 0;
};class circle: public Shape{public:virtual void Draw () const = 0;
};void DrawAllShapes(vector<Shape*> & list)
{vector<Shape*>::iterator I;for (i = list.begin(); i != list.end(); i++) {(*i)->Draw ();}
}

        可以看到,如果我们想要扩展程序9.2中DrawAllShapes函数的行为,使之能够绘制一种新的形状,我们只需要增加一个新的Shape类的派生类.DrawAllShapes函数并不需要改变,这样DrawAllShapes就符合了OCP。无需改动自身代码,就可以扩展它的行为。实际上,增加一个Triangle类对于这里展示的任何模块完全没有影响。很明显,为了能够处理Triangle类,必须要改动系统中的某些部分,但是这里展示的所有代码都无需改动。
      在实际的应用程序中,Sape类可能会有更多的方法。但是在应用程序中增加一种新的形状类型依然非常简单,因为所需要做的工作只是创建Sape类的新的派生类,并实现它的所有函数。再也不需要为了找出需要更改的地方而在应用程序的所有地方进行搜寻。这个解决方案不再是脆弱的。
        同时,这个方案也不再是僵化的。在增加一个新的形状类型时,现有的所有模块的源码都无需改动,并且现有的所有二进制模块都无需进行重新构建(rebuild)。只有一个例外,那就是实际创建Shape类新的派生类实例的模块必须被改动。通常情况下,创建Shape类新的派生类实例的工作要么是在main中或者被main调用的一些函数中完成,要么是在被main创建的一些对象的方法中完成。
        最后,这个方案也不再是牢固的。现在,在任何应用程序中重用DrawAllShapes时,都无需再附带上Square和Circle。因而,这个解决方案就不再具有前面提及的任何糟糕设计的特征。
        这个程序是符合OCP的。对它的改动是通过增加新代码进行的,而不是更改现有的代码。因此,它就不会引起像不遵循OCP的程序那样的连锁改动。所需要的改动仅仅是增加新的模块,以及为了能够实例化新类型的对象而进行的围绕main的改动。

9.3.4 是的,我说谎了

        上面的例子其实并非是100%封闭的!如果我们要求所有的圆必须在正方形之前绘制,那么程序9.2中的DrawAllShapes函数会怎样呢?DrawAllShapes函数无法对这种变化做到封闭。要实现这个需求,我们必须要修改DrawAllShapes的实现,使它首先扫描列表中所有的圆,然后再扫描所有的正方形。

9.3.5 预测变化和“贴切的”结构

        如果我们预测到了这种变化,那么就可以设计一个抽象来隔离它。我们在程序92中所选定的抽象对于这种变化来说反倒成为一种障碍。可能你会觉得奇怪:还有什么比定义一个Shape类,并从它派生出Square类和Cice类更贴切的结构呢?为何这个贴切的模型不是最优的呢?很明显,这个模型对于一个形状的顺序比形状类型具有更重要意义的系统来说,就不再是贴切的了。
        这就导致了一个麻烦的结果,一般而言,无论模块是多么的“封闭”,都会存在一些无法对之封闭的变化。没有对于所有的情况都贴切的模型。
        既然不可能完全封闭,那么就必须有策略地对待这个问题。也就是说,设计人员必须对于他设计的模块应该对哪种变化封闭做出选择。他必须先猜测出最有可能发生的变化种类,然后构造抽象来隔离那些变化。

        这需要设计人员具备一些从经验中获得的预测能力。有经验的设计人员希望自己对用户和应用领域很了解,能够以此来判断各种变化的可能性。然后,他可以让设计对于最有可能发生的变化遵循OCP原则。
        这一点不容易做到。因为它意味着要根据经验猜测那些应用程序在生长历程中有可能遭受的变化。如果开发人员猜测正确,他们就获得成功。如果他们猜测错误,他们会遭受失败。并且在大多数情况下,他们都会猜测错误。
        同时,遵循OCP的代价也是昂贵的。创建正确的抽象是要花费开发时间和精力的。同时,那些抽象也增加了软件设计的复杂性。开发人员有能力处理的抽象的数量也是有限的。显然,我们希望把OCP的应用限定在可能会发生的变化上。
        我们如何知道哪个变化有可能发生呢?我们进行适当的调查,提出正确的问题,并且使用我们的经验和一般常识。最终,我们会一直等到变化发生时才采取行动。

9.3.6 放置吊钩

        我们怎样去隔离变化呢?在上个世纪,我们常常说的一句话是,我们会在我们认为可能发生变化的地方放置吊钩(hook)。我们觉得这样做会使软件灵活一些。
        然而,我们放置的吊钩常常是错误的。更糟的是,即使不使用这些吊钩,也必须要去支持和维护它们,从而就具有了不必要的复杂性的臭味。这不是一件好事。我们不希望设计背着许多不必要的抽象。通常,我们更愿意一直等到确实需要那些抽象时再把它放置进去。

1.只受一次愚弄

        有句古老的谚语说:“愚弄我一次,应感羞愧的是你。再次愚弄我,应感羞愧的是我。”这也是一种有效的对待软件设计的态度。为了防止软件背着不必要的复杂性,我们会允许自己被愚弄一次。这意味着在我们最初编写代码时,假设变化不会发生。当变化发生时,我们就创建抽象来隔离以后发生的同类变化。简而言之,我们愿意被第一颗子弹击中,然后我们会确保自己不再被同一只枪发射的其他任何子弹击中。

2.刺激变化

        如果我们决定接受第一颗子弹,那么子弹到来的越早、越快就对我们越有利。我们希望在开发工作展开不久就知道可能发生的变化。查明可能发生的变化所等待的时间越长,要创建正确的抽象就越困难。
        因此,我们需要去刺激变化。我们已在第2章中讲述的一些方法来完成这项工作。
        (1)我们首先编写测试。测试描绘了系统的一种使用方法。通过首先编写测试,我们迫使系统成为可测试的。在一个具有可测试性的系统中发生变化时,我们可以坦然对之。因为我们已经构建了使系统可测试的抽象。并且通常这些抽象中的许多都会隔离以后发生的其他种类的变化。
        (2)我们使用很短的迭代周期进行开发个周期为几天而不是几周。
        (3)我们在加入基础结构前就开发特性,并且经常性地把那些特性展示给涉众。
        (4)我们首先开发最重要的特性。
        (5)尽早地、经常性地发布软件。尽可能快地、尽可能频繁地把软件展示给客户和使用人员。

9.3.7 使用抽象获得显示封闭

        第一颗子弹已经击中我们,用户要求我们在绘制正方形之前先绘制所有的圆。现在我们希望可以隔离以后所有的同类变化。
        怎样才能使得DrawAllShapes函数对于绘制顺序的变化是封闭的呢?请记住封闭是建立在抽象的基础之上的。因此,为了让DrawAllShapes对于绘制顺序的变化是封闭的,我们需要一种“顺序抽象体”。这个抽象体定义了一个抽象接口,通过这个抽象接口可以表示任何可能的排序策略。
        一个排序策略意味着,给定两个对象可以推导出应该先绘制哪一个。我们可以定义一个Shpe类的抽象方法叫作Precedes.。这个方法以另外一个Shape作为参数,并返回-一个bool型结果。如果接收消息的Shape对象应该先于作为参数传入的Shape对象绘制,那么函数返回true。
        在C++中,这个函数可以通过重载operator<来表示。程序9.3中展示了添加了排序方法后的Shape类。
        既然我们已经有了决定两个Shape对象的绘制顺序的方法,我们就可以对列表中的shape对象进行排序后依序绘制。程序9.4展示了C++的实现代码。

图9.3.7.1

         这给我们提供了一种对Shape对象排序的方法,也使得可以按照一定的顺序来绘制它们。但是我们仍然没有一个好的用来排序的抽象体。按照目前的设计,Shape对象应该覆写Precedes方法来指定顺序。这究竟是如何工作的呢?我们应该在Circle:Precedes成员函数中编写一些什么代码,来保证圆一定会被先于正方形绘制呢?请看程序9.5。

 图9.3.7.2

        显然这个函数以及所有Shape类的派生类中的Precedes函数都不符合OCP。没有办法使得这些函数对于Shape类的新派生类做到封闭。每次创建一个新的Shape类的派生类时,所有的Precedest)函数都需要改动。

        当然,如果从来不需创建新的Shape类的派生类,就没有关系了。另一方面,如果需要频繁的创建新的Sape类的派生类,这个设计就会遭到沉重的打击。我们再次被第一颗子弹击中。

9.3.8 使用“数据驱动”的方法获取封闭性

        如果我们要使Shape类的各个派生类间互不知晓,可以使用表格驱动的方法。程序9.6展示了一·种可能的实现。

         通过这种方法,我们成功地做到了一般情况下DrawAllShapes函数对于顺序问题的封闭,也使得每个Shape派生类对于新的Shape派生类的创建或者基于类型的Shape对象排序规则的改变是封闭的。(比如,改变顺序为正方形必须最先绘制。)

        对于不同的Shapes的绘制顺序的变化不封闭的惟-一部分就是表本身。可以把表放置在一个单独的模块中,和所有其他模块隔离,因此对于表的改动不会影响到其他任何模块。事实上,在C++中,我们可以在链接时选择要使用的表。

9.4 结论

        在许多方面,OCP都是面向对象设计的核心所在。遵循这个原则可以带来面向对象技术所声称的巨大好处(也就是,灵活性、可重用性以及可维护性)。然而,并不是说只要使用一种面向对象语言就是遵循了这个原则。对于应用程序中的每个部分都肆意地进行抽象同样不是一个好主意。正确的做法是,开发人员应该仅仅对程序中呈现出频繁变化的那些部分做出抽象。拒绝不成熟的抽象和抽象本身一样重要。

相关文章:

敏捷开发笔记(第9章节)--开放-封闭原则(OCP)

目录 1&#xff1a;PDF上传链接 9.1 开放-封闭原则&#xff08;OCP&#xff09; 9.2 描述 9.3 关键是抽象 9.3.1 shape应用程序 9.3.2 违反OCP 糟糕的设计 9.3.3 遵循OCP 9.3.4 是的&#xff0c;我说谎了 9.3.5 预测变化和“贴切的”结构 9.3.6 放置吊钩 1.只受一次…...

苹果电脑清理app垃圾高效清理,无需专业知识

在我们的日常使用中&#xff0c;苹果电脑以其优雅的设计和强大的功能赢得了广泛的喜爱。然而&#xff0c;即便是最高效的设备&#xff0c;也无法免俗地积累各种不必要的文件和垃圾&#xff0c;特别是app垃圾。所以&#xff0c;苹果电脑清理app垃圾高效清理&#xff0c;对于大多…...

【算法】(C语言):快速排序(递归)、归并排序(递归)、希尔排序

快速排序&#xff08;递归&#xff09; 左指针指向第一个数据&#xff0c;右指针指向最后一个数据。取第一个数据作为中间值。右指针指向的数据 循环与中间值比对&#xff0c;若大于中间值&#xff0c;右指针往左移动一位&#xff0c;若小于中间值&#xff0c;右指针停住。右…...

模型驱动开发(Model-Driven Development,MDD):提高软件开发效率与一致性的利器

目录 前言1. 模型驱动开发的原理1.1 什么是模型驱动开发1.2 MDD的核心思想 2. 模型驱动开发的优势2.1 提高开发效率2.2 确保代码一致性2.3 促进沟通和协作2.4 方便维护和扩展 3. 实现模型驱动开发的方法3.1 选择合适的建模工具3.1.1 UML3.1.2 BPMN3.1.3 SysML 3.2 建模方法3.2.…...

记录discuz修改用户的主题出售价格

大家好&#xff0c;我是网创有方的站长&#xff0c;今天遇到了需要修改discuz的主题出售价格。特此记录下 方法很简单&#xff1a; 进入用于组-》选择论坛-》批量修改...

WGAN(Wassertein GAN)

WGAN E x ∼ P g [ log ⁡ ( 1 − D ( x ) ) ] E x ∼ P g [ − log ⁡ D ( x ) ] \begin{aligned} & \mathbb{E}_{x \sim P_g}[\log (1-D(x))] \\ & \mathbb{E}_{x \sim P_g}[-\log D(x)] \end{aligned} ​Ex∼Pg​​[log(1−D(x))]Ex∼Pg​​[−logD(x)]​ 原始 GAN …...

Maven基本使用

1. Maven前瞻 Maven官网&#xff1a;https://maven.apache.org/ Maven镜像&#xff1a;https://mvnrepository.com 1.1、Maven是什么 Maven是一个功能强大的项目管理和构建工具&#xff0c;可以帮助开发人员简化Java项目的构建过程。 在Maven中&#xff0c;使用一个名为 pom.…...

在Linux系统中配置GitHub的SSH公钥

在Linux系统中配置GitHub的SSH公钥&#xff0c;可以让您无需频繁输入密码即可与GitHub仓库进行交互&#xff0c;提高工作效率。以下是配置步骤: 第一步&#xff1a; 检查SSH密钥是否存在 首先&#xff0c;检查您的用户目录下的.ssh文件夹中是否已有SSH密钥。打开终端&#xff0…...

小酌消烦暑|人间正清欢

小暑是二十四节气之第十一个节气。暑&#xff0c;是炎热的意思&#xff0c;小暑为小热&#xff0c;还不十分热。小暑虽不是一年中最炎热的时节&#xff0c;但紧接着就是一年中最热的节气大暑&#xff0c;民间有"小暑大暑&#xff0c;上蒸下煮"之说。中国多地自小暑起…...

C语言结构体的相关知识

前言 从0开始记录我的学习历程&#xff0c;我会尽我所能&#xff0c;写出最最大白话的文章&#xff0c;希望能够帮到你&#xff0c;谢谢。 1.结构体类型的概念及定义 1.1、概念&#xff1a; 结构体是一种构造类型的数据结构&#xff0c; 是一种或多种基本类型或构造类型的数…...

RabbitMQ入门教程(精细版二带图)

目录 六 RabbitMQ工作模式 6.1Hello World简单模式 6.1.1 什么是简单模式 6.1.2 RabbitMQ管理界面操作 6.1.3 生产者代码 6.1.4 消费者代码 6.2 Work queues工作队列模式 6.2.1 什么是工作队列模式 6.2.2 RabbitMQ管理界面操作 6.2.3 生产者代码 6.2.4 消费者代码 …...

IO、零拷贝、多路复用、connection、池化

目录 一、IO 模型 二、什么是网络IO 三、什么是零拷贝 四、多路复用 五、java程序、mysql JDBC connection关系 六、connection怎么操作事务 七 、java里面的池化技术 八、线程池7个核心参数 九、线程的状态 一、IO 模型 BIO &#xff1a;同步阻塞io&#xff0c;单线程 内存上下…...

Lua 错误处理

Lua 错误处理 Lua是一种轻量级的编程语言&#xff0c;广泛用于游戏开发、脚本编写和其他应用程序中。在编程过程中&#xff0c;错误处理是一个重要的方面&#xff0c;它可以帮助开发者创建更健壮和可靠的程序。本文将详细介绍Lua中的错误处理机制。 错误类型 在Lua中&#x…...

二刷力扣——单调栈

739. 每日温度 单调栈应该从栈底到栈顶 是递减的。 找下一个更大的 &#xff0c;用递减单调栈&#xff0c;就可以确定在栈里面的每个比当前元素i小的元素&#xff0c;下一个更大的就是这个i&#xff0c;然后弹出并记录&#xff1b;然后当前元素i入栈&#xff0c;仍然满足递减…...

elementPlus-vue3-ts表格单选和双选实现方式

记录在vue3、ts、element-plus环境下表格单选和多选的实现方式 单选 html部分 <el-table...reftaskTableRefselect"selectClick"... ><el-table-column type"selection" width"50" />... </el-table>ts部分 const taskTabl…...

Linux系统中卸载GitLab

在Linux系统中卸载GitLab&#xff0c;主要可以通过包管理器&#xff08;如apt、yum、rpm等&#xff09;来实现&#xff0c;但具体步骤可能会因GitLab的安装方式&#xff08;如使用包管理器安装、从源代码安装、使用Docker等&#xff09;和Linux发行版的不同而有所差异。以下是一…...

基于STM32F407ZG的FreeRTOS移植

1.从FreeRTOS官网中下载源码 2、简单分析FreeRTOS源码目录结构 2.1、简单分析FreeRTOS源码根目录 &#xff08;1&#xff09;Demo&#xff1a;是官方为一些单片机移植FreeRTOS的例程 &#xff08;2&#xff09;License&#xff1a;许可信息 &#xff08;3&#xff09;Sourc…...

【IT领域新生必看】Java编程中的神奇对比:深入理解`equals`与`==`的区别

文章目录 引言什么是操作符&#xff1f;基本数据类型的比较示例&#xff1a; 引用类型的比较示例&#xff1a; 什么是equals方法&#xff1f;equals方法的默认实现示例&#xff1a; 重写equals方法示例&#xff1a; equals与的区别比较内容不同示例&#xff1a; 使用场景不同示…...

WEBHTTP

目录 理解HTTP协议请求流程 1 1 Web基础 2 Hosts文件 1 1 2网页与HTML 2 HTML概述 1 1 3静态网页与动态网页 1.2HTTP协议 1 2 1 HTTP协议概述 1 2 2 HTTP方法 HTTP支持几种不同的请求命令&#xff0c;这些命令被称为HTTP方法(HTTP method 表1一3 HTTP方法 表1&#…...

nodejs 获取客服端ip,以及获取ip一直都是127.0.0.1的问题

一、问题描述 在做登录日志的时候想要获取客户端的ip, 网上查了一下 通过 req.headers[x-forwarded-for] || req.connection.remoteAddress; 获取&#xff0c; 结果获取了之后不管是开发环境&#xff0c;还是生产环境获取到的一直都是 127.0.0.1&#xff0c;这是因为在配置N…...

微软与OpenAI/谷歌与三星的AI交易受欧盟重点关注

近日&#xff0c;欧盟委员会主管竞争事务的副主席玛格丽特维斯塔格(Margrethe Vestager)在一次演讲中透露&#xff0c;欧盟反垄断监管机构将就微软与OpenAI的合作&#xff0c;以及谷歌与三星达成的AI协议寻求更多第三方意见。这意味着微软与 OpenAI、谷歌与三星的 AI 交易及合作…...

微信小程序毕业设计-学生实习与就业管理系统项目开发实战(附源码+论文)

大家好&#xff01;我是程序猿老A&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f49e;当前专栏&#xff1a;微信小程序毕业设计 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f380; Python毕业设计…...

spring boot 接口参数解密和返回值加密

spring boot 接口参数解密和返回值加密 开发背景简介安装配置yml 方式Bean 方式 试一下启动项目返回值加密参数解密body 参数解密param和form-data参数解密 总结 开发背景 虽然使用 HTTPS 已经可以基本保证传输数据的安全性&#xff0c;但是很多国企、医疗、股票项目等仍然要求…...

C语言自定义类型——联合体、枚举

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、联合体&#xff08;一&#xff09;、联合体的声明&#xff08;二&#xff09;、联合体的特点&#xff08;三&#xff09;、联合体大小的计算&#xff01;&a…...

【trition-server】pytorch 文档:使用 Triton 提供 Torch-TensorRT 模型

Serving a Torch-TensorRT model with Triton pytorch 的官方文档: Serving a Torch-TensorRT model with Triton 在有关机器学习基础设施的讨论中,优化和部署是密不可分的。一旦完成网络级优化以获得最大性能,下一步就是部署它。 然而,提供这种优化模型也有其自身的一系列…...

wps 表格如何实现vlookup高级模糊搜索

一、VLOOKUP 模糊搜索 在 WPS 表格中&#xff0c;可以通过使用 VLOOKUP 函数和通配符来实现高级模糊搜索。这里有一个具体的示例来帮助你理解如何进行这些操作。 示例&#xff1a;实现 VLOOKUP 高级模糊搜索 假设我们有以下数据集&#xff1a; AB产品编号产品名称001苹果00…...

第一天(点亮led灯+led灯闪烁)——Arduino uno R3 学习之旅

​ 常识: 一般智能手机的额定工作电流大约为200mA Arduino Uno板上I/0(输入/输出)引脚最大输出电流为40 mA Uno板控制器总的输出电流为200 mA 点亮LED灯 发光二极管介绍 发光二极管(Light Emitting Diode&#xff0c;简称LED)是一种能够将电能转化为光能的固态的半导体器件…...

【C++题解】1561. 买木头

问题&#xff1a;1561. 买木头 类型&#xff1a;省赛、数组问题、二分答案、贪心、2015江苏省青少年信息学奥林匹克竞赛复赛 题目描述&#xff1a; 有 n 个木材供应商&#xff0c;每个供货商有长度相同一定数量的木头。长木头可以锯短&#xff0c;但短木头不能接长。有一个客…...

解决android native包webview,webview中的请求blocked by CORS policy

在stack overflow查&#xff0c;差不多查到的都是些webView.getSettings().setxxx&#xff0c;没用。在github上找别的类似的android native包webview运行pwa的项目&#xff0c;把它们的webView.getSettings().setxxx全搬过来&#xff0c;写了有一页多&#xff0c;一个有用的都…...

链篦机回转窑球团生产工艺

生球在回转窑氧化焙烧&#xff0c;回转窑头部设有燃烧器&#xff0c;燃料可以采用气体、固体、液体。 来自环冷机一冷却段的高温废气作为二次风进入窑内参与燃烧&#xff0c;烧成成品球进入环冷机。 环冷机采用鼓风冷却&#xff0c;热风风箱分为四段&#xff1a; 一段气体引至…...

查看电脑ip地址快捷键是什么?是哪个

在网络世界中&#xff0c;IP地址是每个网络设备的唯一标识&#xff0c;无论是我们的电脑、手机还是其他联网设备&#xff0c;都需要一个独特的IP地址来进行通讯。在日常生活和工作中&#xff0c;我们有时需要查看电脑的IP地址&#xff0c;以便进行网络设置、故障排查或远程连接…...

面试专区|【54道Spring Cloud高频题整理(附答案背诵版)】

什么是Spring Cloud&#xff1f; Spring Cloud是一个基于Spring Boot的开源框架&#xff0c;它提供了在分布式系统中集成各种服务治理功能的工具&#xff0c;如配置管理、服务发现、断路器、智能路由、微代理、控制总线、全局锁、决策竞选、分布式会话和集群状态等。其主要目…...

Shopee(虾皮)怎么获取流量?

店铺流量的高低会直接关联到卖家店铺单量&#xff0c;也关系到一个店铺的营业情况和利润&#xff0c;那么Shopee的流量从哪里来呢&#xff1f; Shopee的平台流量可分为五个部分&#xff1a; 1.自然流量 2.关键字广告流量 3.平台活动流量 4.营销流量 5.粉丝流量 怎么提升…...

Java启动虚拟机默认字符集编码

-Dfile.encodingUTF-8 java程序启动默认字符集编码参数 // 这里会创建一个Charset.defaultCharset().name()的流&#xff0c;在Windows命令行窗口启动&#xff0c;会出现字符编码为GBK的情况 // 导致乱码输入、输出都会有影响 // 解决办法流的读取指定编码new InputStreamRead…...

【单片机编程模式】状态机编程

状态机编程是一种编程模式&#xff0c;它基于有限状态机&#xff08;Finite State Machine&#xff0c;简称FSM&#xff09;的概念。以下是状态机编程的清晰解释&#xff0c;分点表示和归纳&#xff1a; 基本概念&#xff1a; 状态机是一个有向图形&#xff0c;由一组节点&…...

IPSS模块怎么安装到VOS服务器的,到底有没有效果,是不是能大幅度提升VOS3000安全性呢

由于VOS的普及性&#xff0c;不得不承认VOS确实是非常优秀的软交换&#xff0c;但是很多客户在使用过程中都会遇到各种安全问题&#xff0c;比如话费被盗用了&#xff0c;历史话单一堆的非法呼叫话单&#xff0c;严重的影响到了话务安全&#xff0c;并不是那点话费的事了&#…...

C++ STL容器:序列式容器-堆pirority_queue

摘要&#xff1a; CC STL&#xff08;Standard Template Library&#xff0c;标准模板库&#xff09;在C编程中的重要性不容忽视&#xff0c;STL提供了一系列容器、迭代器、算法和函数对象&#xff0c;这些组件极大地提高了C程序的开发效率和代码质量。 STL 容器 分为 2 大类 …...

ECharts在最新版本中使用getInstanceByDom报错处理

引用问题导致报错 如果按如下引用的话&#xff0c;会报错 import echarts from “echarts/lib/echarts”; 原因 在 ECharts 的之前版本中&#xff0c;默认导出了一个名为 echarts 的对象&#xff0c;所以使用 import echarts from “echarts” 是没有问题的。但是在 ECharts …...

利用C语言实现三子棋游戏

文章目录 1.游戏界面2.游戏内容2.1 棋盘类型2.2棋盘的初始化2.3 打印棋盘的界面展示 3.游戏操作3.1 玩家操作3.2 电脑操作3.3 胜负判定 4.代码整合 1.游戏界面 无论写任何程序&#xff0c;我们都需要先去了解它的大概框架&#xff0c;这里我们先把它的初始界面写出来。一个游戏…...

大学教师门诊预约小程序-计算机毕业设计源码73068

摘要 在当今数字化、信息化的浪潮中&#xff0c;大学校园的服务管理正朝着智能化、便捷化的方向迈进。为了优化大学教师的医疗体验&#xff0c;提升门诊预约的效率和便捷性&#xff0c;我们基于Spring Boot框架设计并实现了一款大学教师门诊预约小程序。该小程序不仅提供了传统…...

Python PyCryptodome库介绍与实例

Python PyCryptodome库介绍与实例 1. 安装2. 基本概念3. 使用场景和示例代码3.1 对称加密 - AES3.2 非对称加密 - RSA3.3 哈希函数 - SHA2563.4 消息认证码 - HMAC 4. 总结 PyCryptodome是一个强大的Python加密库,提供了各种加密算法和工具。本文将介绍PyCryptodome的基本概念和…...

《框架封装者 · 自定义初始化事件》

&#x1f4e2; 大家好&#xff0c;我是 【战神刘玉栋】&#xff0c;有10多年的研发经验&#xff0c;致力于前后端技术栈的知识沉淀和传播。 &#x1f497; &#x1f33b; CSDN入驻不久&#xff0c;希望大家多多支持&#xff0c;后续会继续提升文章质量&#xff0c;绝不滥竽充数…...

ActiViz实战:使用vtkImageClip和vtkImageActor根据滑动条来显示当前图像数据切面

文章目录 一、效果预览二、代码实现三、源码地址一、效果预览 ActiViz实现图像数据切面显示 二、代码实现 public partial class Form1 : Form {private vtkRenderWindowInteractor _interactor;private vtkRenderer _renderer...

【论文笔记】BEVCar: Camera-Radar Fusion for BEV Map and Object Segmentation

原文链接&#xff1a;https://arxiv.org/abs/2403.11761 0. 概述 本文的BEVCar模型是基于环视图像和雷达融合的BEV目标检测和地图分割模型&#xff0c;如图所示。模型的图像分支利用可变形注意力&#xff0c;将图像特征提升到BEV空间中&#xff0c;其中雷达数据用于初始化查询…...

圆通寄15kg30kg一般多少钱?寄大件物品怎么寄最便宜?

作为一名即将毕业的大学生&#xff0c;搬家成了我和室友们共同的难题。尤其是在寄送大件物品时&#xff0c;如何省钱、如何打包、选择哪家快递公司等问题让我们头疼不已。今天&#xff0c;我就来分享一些寄大件物品的省钱技巧以及打包方法&#xff0c;希望对大家有所帮助。 一…...

transformer初探

transformer初探 self-attentionmultihead-attentionencoderdecoder self-attention 其实就是三个矩阵&#xff0c; W q W_q Wq​、 W k W_k Wk​、 W v W_v Wv​&#xff0c;这三个矩阵就是需要训练的参数。分别得到每个token对应的 q q q k k k v v v&#xff0c;其中 q …...

JUC并发编程基础(包含线程概念,状态等具体实现)

一.JUC并发编程基础 1. 并行与并发 1.1 并发: 是在同一实体上的多个事件是在一台处理器上"同时处理多个任务"同一时刻,其实是只有一个事件在发生. 即多个线程抢占同一个资源. 1.2 并行 是在不同实体上的多个事件是在多台处理器上同时处理多个任务同一时刻,大家…...

集中管理和分析日志:使用 ELK 套件构建强大的日志管理平台

集中管理和分析日志&#xff1a;使用 ELK 套件构建强大的日志管理平台 日志是监控和调试应用程序和系统的重要工具。集中管理和分析日志可以帮助你快速定位问题、了解系统运行状况和性能&#xff0c;并提高你的日志管理效率。ELK 是一个流行的日志管理解决方案&#xff0c;由 …...

深度学习 - 模型的保存与部署方式汇总

深度学习模型保存和加载格式科普 在深度学习中&#xff0c;模型的保存和加载是非常重要的环节。不同的格式有不同的特点和适用场景。本文将为新手朋友们介绍几种常见的模型格式&#xff0c;包括它们的简介、保存方式、加载方式、优缺点以及应用场景。 1. PyTorch (.pth, .pt)…...

人工智能对网络安全有何影响?

人工智能网络安全在短期、中期和长期如何变化 当今数字时代网络安全的重要性 在谈论人工智能在网络安全中的作用时&#xff0c;必须首先考虑短期影响&#xff0c;因为它们是最明显的&#xff0c;而且它是一个未知的领域&#xff0c;需要超越直接炒作的能力。 因此&#xff0…...