- 浏览: 877219 次
- 性别:
- 来自: 北京
文章分类
最新评论
-
Junjing:
非常感谢楼主的分享,受益匪浅!我是一位从业务规划和运营转需求分 ...
我们应当怎样做需求确认:评审与签字确认会 -
kersky:
感谢楼主的辛苦输出,半天看完了整个系列。对于一个转从开发转需求 ...
我们应当怎样做需求确认:评审与签字确认会 -
DEMONU:
必须要顶
谈谈软件开发的那些事儿 之 软件开发的轮回 -
dripstone:
非常感谢楼主,用了大半天的时间,一口气读完了需求分析阶段。好多 ...
我们应当怎样做需求确认:评审与签字确认会 -
Codepoe:
做了一些开发,看了楼主的文章,我深有感触,为自己的做法找到了理 ...
我们应当改变我们的设计习惯
3.可变更性
前面我提到了,软件的变更性是所有软件理论的核心,那么什么是软件的可变更性呢?按照现在的软件理论,客户对软件的需求时时刻刻在发生着变化。当软件设计好以后,为应对客户需求的变更而进行的代码修改,其所需要付出的代价,就是软件设计的可变更性。由于软件合理地设计,修改所付出的代价越小,则软件的可变更性越好,即代码设计的质量越高。一种非常理想的状态是,无论客户需求怎样变化,软件只需进行适当地修改就能够适应。但这之所以称之为理想状态,因为客户需求变化是有大有小的。如果客户需求变化非常大,即使再好的设计也无法应付,甚至重新开发。然而,客户需求的适当变化,一个合理地设计可以使得变更代价最小化,延续我们设计的软件的生命力。
1)通过提高代码复用提高可维护性
我曾经遇到过这样一件事,我要维护的一个系统因为应用范围的扩大,它对机关级次的计算方式需要改变一种策略。如果这个项目统一采用一段公用方法来计算机关级次,这样一个修改实在太简单了,就是修改这个公用方法即可。但是,事实却不一样,对机关级次计算的代码遍布整个项目,甚至有些还写入到了那些复杂的SQL语句中。在这样一种情况下,这样一个需求的修改无异于需要遍历这个项目代码。这样一个实例显示了一个项目代码复用的重要,然而不幸的是,代码无法很好复用的情况遍布我们所有的项目。代码复用的道理十分简单,但要具体运作起来非常复杂,它除了需要很好的代码规划,还需要持续地代码重构。
对整个系统的整体分析与合理规划可以根本地保证代码复用。系统分析师通过用例模型、领域模型、分析模型的一步一步分析,最后通过正向工程,生成系统需要设计的各种类及其各自的属性和方法。采用这种方法,功能被合理地划分到这个类中,可以很好地保证代码复用。
采用以上方法虽然好,但技术难度较高,需要有高深的系统分析师,并不是所有项目都能普遍采用的,特别是时间比较紧张的项目。通过开发人员在设计过程中的重构,也许更加实用。当某个开发人员在开发一段代码时,发现该功能与前面已经开发功能相同,或者部分相同。这时,这个开发人员可以对前面已经开发的功能进行重构,将可以通用的代码提取出来,进行相应地改造,使其具有一定的通用性,便于各个地方可以使用。
一些比较成功的项目组会指定一个专门管理通用代码的人,负责收集和整理项目组中各个成员编写的,可以通用的代码。这个负责人同时也应当具有一定的代码编写功力,因为将专用代码提升为通用代码,或者以前使用该通用代码的某个功能,由于业务变更,而对这个通用代码的变更要求,都对这个负责人提出了很高的能力要求。
虽然后一种方式非常实用,但是它有些亡羊补牢的味道,不能从整体上对项目代码进行有效规划。正因为两种方法各有利弊,因此在项目中应当配合使用。
2)利用设计模式提高可变更性
对于初学者,软件设计理论常常感觉晦涩难懂。一个快速提高软件质量的捷径就是利用设计模式。这里说的设计模式,不仅仅指经典的32个模式,是一切前人总结的,我们可以利用的、更加广泛的设计模式。
a. if...else...
这个我也不知道叫什么名字,最早是哪位大师总结的,它出现在Larman的《UML与模式应用》,也出现在出现在Mardin的《敏捷软件开发》。它是这样描述的:当你发现你必须要设计这样的代码:“if...elseif...elseif...else...”时,你应当想到你的代码应当重构一下了。我们先看看这样的代码有怎样的特点。
if(var.equals("A")){ doA(); }
else if(var.equals("B")){ doB(); }
else if(var.equals("C")){ doC(); }
else{ doD(); }
这样的代码很常见,也非常平常,我们大家都写过。但正是这样平常才隐藏着我们永远没有注意的问题。问题就在于,如果某一天这个选项不再仅仅是A、B、C,而是增加了新的选项,会怎样呢?你也许会说,那没有关系,我把代码改改就行。然而事实上并非如此,在大型软件研发与维护中有一个原则,每次的变更尽量不要去修改原有的代码。如果我们重构一下,能保证不修改原有代码,仅仅增加新的代码就能应付选项的增加,这就增加了这段代码的可维护性和可变更性,提高了代码质量。那么,我们应当如何去做呢?
经过深入分析你会发现,这里存在一个对应关系,即A对应doA(),B对应doB()...如果将doA()、doB()、doC()...与原有代码解耦,问题就解决了。如何解耦呢?设计一个接口X以及它的实现A、B、C...每个类都包含一个方法doX(),并且将doA()的代码放到A.doX()中,将doB()的代码放到B.doX()中...经过以上的重构,代码还是这些代码,效果却完全不一样了。我们只需要这样写:
X x = factory.getBean(var); x.doX();
这样就可以实现以上的功能了。我们看到这里有一个工厂,放着所有的A、B、C...并且与它们的key对应起来,并且写在配置文件中。如果出现新的选项时,通过修改配置文件就可以无限制的增加下去。
这个模式虽然有效提高了代码质量,但是不能滥用,并非只要出现if...else...就需要使用。由于它使用了工厂,一定程度上增加了代码复杂度,因此仅仅在选项较多,并且增加选项的可能性很大的情况下才可以使用。另外,要使用这个模式,继承我在附件中提供的抽象类XmlBuildFactoryFacade就可以快速建立一个工厂。如果你的项目放在spring或其它可配置框架中,也可以快速建立工厂。设计一个Map静态属性并使其V为这些A、B、C...这个工厂就建立起来了。
b.策略模式
也许你看过策略模式(strategy model)的相关资料但没有留下太多的印象。一个简单的例子可以让你快速理解它。如果一个员工系统中,员工被分为临时工和正式工并且在不同的地方相应的行为不一样。在设计它们的时候,你肯定设计一个抽象的员工类,并且设计两个继承类:临时工和正式工。这样,通过下塑类型,可以在不同的地方表现出临时工和正式工的各自行为。在另一个系统中,员工被分为了销售人员、技术人员、管理人员并且也在不同的地方相应的行为不一样。同样,我们在设计时也是设计一个抽象的员工类,并且设计数个继承类:销售人员、技术人员、管理人员。现在,我们要把这两个系统合并起来,也就是说,在新的系统中,员工既被分为临时工和正式工,又被分为了销售人员、技术人员、管理人员,这时候如何设计。如果我们还是使用以往的设计,我们将不得不设计很多继承类:销售临时工、销售正式工、技术临时工、技术正式工...如此的设计,在随着划分的类型,以及每种类型的选项的增多,呈笛卡尔增长。通过以上一个系统的设计,我们不得不发现,我们以往学习的关于继承的设计遇到了挑战。
解决继承出现的问题,有一个最好的办法,就是采用策略模式。在这个应用中,员工之所以要分为临时工和正式工,无非是因为它们的一些行为不一样,比如,发工资时的计算方式不同。如果我们在设计时不将员工类分为临时工类和正式工类,而仅仅只有员工类,只是在类中增加“工资发放策略”。当我们创建员工对象时,根据员工的类型,将“工资发放策略”设定为“临时工策略”或“正式工策略”,在计算工资时,只需要调用策略类中的“计算工资”方法,其行为的表现,也设计临时工类和正式工类是一样的。同样的设计可以放到销售人员策略、技术人员策略、管理人员策略中。一个通常的设计是,我们将某一个影响更大的、或者选项更少的属性设计成继承类,而将其它属性设计成策略类,就可以很好的解决以上问题。
使用策略模式,你同样把代码写活了,因为你可以无限制地增加策略。但是,使用策略模式你同样需要设计一个工厂——策略工厂。以上实例中,你需要设计一个发放工资策略工厂,并且在工厂中将“临时工”与“临时工策略”对应起来,将“正式工”与“正式工策略”对应起来。
c.适配器模式
我的笔记本是港货,它的插头与我们常用的插座不一样,所有我出差的时候我必须带一个适配器,才能使用不同地方的插座。这是一个对适配器模式最经典的描述。当我们设计的系统要与其它系统交互,或者我们设计的模块要与其它模块交互时,这种交互可能是调用一个接口,或者交换一段数据,接受方常常因发送方对协议的变更而频繁变更。这种变更,可能是接受方来源的变更,比如原来是A系统,现在变成B系统了;也可能是接受方自身的代码变更,如原来的接口现在增加了一个参数。由于发送方的变更常常导致接受方代码的不稳定,即频繁跟着修改,为接受方的维护带来困难。
遇到这样的问题,一个有经验的程序员马上想到的就是采用适配器模式。在设计时,我方的接口按照某个协议编写,并且保持固定不变。然后,在与真正对方接口时,在前段设计一个适配器类,一旦对方协议发生变更,我可以换个适配器,将新协议转换成原协议,问题就解决了。适配器模式应当包含一个接口和它的实现类。接口应当包含一个本系统要调用的方法,而它的实现类分别是与A系统接口的适配器、与B系统接口的适配器...
我曾经在一个项目中需要与另一个系统接口,起初那个系统通过一个数据集的方式为我提供数据,我写了一个接收数据集的适配器;后来改为用一个XML数据流的形式,我又写了一个接收XML的适配器。虽然为我提供数据的方式不同,但是经过适配器转换后,输出的数据是一样的。通过在spring中的配置,我可以灵活地切换到底是使用哪个适配器。
d.模板模式
32个经典模式中的模板模式,对开发者的代码规划能力提出了更高的要求,它要求开发者对自己开发的所有代码有一个相互联系和从中抽象的能力,从各个不同的模块和各个不同的功能中,抽象出其过程比较一致的通用流程,最终形成模板。譬如说,读取XML并形成工厂,是许多模块常常要使用的功能。它们虽然有各自的不同,但是总体流程都是一样的:读取XML文件、解析XML数据流、形成工厂。正因为有这样的特征,它们可以使用共同的模板,那么,什么是模板模式呢?
模板模式(Template Model)通常有一个抽象类。在这个抽象类中,通常有一个主函数,按照一定地顺序去调用其它函数。而其它函数往往是某这个连续过程中的各个步骤,如以上实例中的读取XML文件、解析XML数据流、形成工厂等步骤。由于这是一个抽象类,这些步骤函数可以是抽象函数。抽象类仅仅定义了整个过程的执行顺序,以及一些可以通用的步骤(如读取XML文件和解析XML数据流),而另一些比较个性的步骤,则由它的继承类自己去完成(如上例中的“形成工厂”,由于各个工厂各不一样,因此由各自的继承类自己去决定它的工厂是怎样形成的)。
各个继承类可以根据自己的需要,通过重载重新定义各个步骤函数。但是,模板模式要求不能重载主函数,因此正规的模板模式其主函数应当是final(虽然我们常常不这么写)。另外,模板模式还允许你定义的这个步骤中,有些步骤是可选步骤。对与可选步骤,我们通常称为“钩子(hood)”。它在编写时,在抽象类中并不是一个抽象函数,但却是一个什么都不写的空函数。继承类在编写时,如果需要这个步骤则重载这个函数,否则就什么也不写,进而在执行的时候也如同什么都没有执行。
通过以上对模板模式的描述可以发现,模板模式可以大大地提高我们的代码复用程度。
以上一些常用设计模式,都能使我们快速提高代码质量。还是那句话,设计模式不是什么高深的东西,恰恰相反,它是初学者快速提高的捷径。然而,如果说提高代码复用是提高代码质量的初阶,使用设计模式也只能是提高代码质量的中阶。那么,什么是高阶呢?我认为是那些分析设计理论,更具体地说,就是职责驱动设计和领域驱动设计。
评论
受教了
群众的眼睛是雪亮的
<div class="quote_div">
<span>d.</span><span>外观模式</span>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 27pt;"><span style="font-size: small;"><span lang="EN-US"><span style="font-family: Times New Roman;">32</span></span><span>个经典模式中的外观模式,对开发者的代码规划能力提出了更高的要求,它要求开发者对自己开发的所有代码有一个相互联系和从中抽象的能力,从各个不同的模块和各个不同的功能中,抽象出其过程比较一致的通用流程,最终形成外观。譬如说,读取</span><span lang="EN-US"><span style="font-family: Times New Roman;">XML</span></span><span>并形成工厂,是许多模块常常要使用的功能。它们虽然有各自的不同,但是总体流程都是一样的:读取</span><span lang="EN-US"><span style="font-family: Times New Roman;">XML</span></span><span>文件、解析</span><span lang="EN-US"><span style="font-family: Times New Roman;">XML</span></span><span>数据流、形成工厂。正因为有这样的特征,它们可以使用共同的外观,那么,什么是外观模式呢?</span></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 27pt;"><span style="font-size: small;"><span>外观模式(</span><span lang="EN-US"><span style="font-family: Times New Roman;">Façade Model</span></span><span>)通常有一个抽象类。在这个抽象类中,通常有一个主函数,按照一定地顺序去调用其它函数。而其它函数往往是某这个连续过程中的各个步骤,如以上实例中的读取</span><span lang="EN-US"><span style="font-family: Times New Roman;">XML</span></span><span>文件、解析</span><span lang="EN-US"><span style="font-family: Times New Roman;">XML</span></span><span>数据流、形成工厂等步骤。由于这是一个抽象类,这些步骤函数可以是抽象函数。抽象类仅仅定义了整个过程的执行顺序,以及一些可以通用的步骤(如读取</span><span lang="EN-US"><span style="font-family: Times New Roman;">XML</span></span><span>文件和解析</span><span lang="EN-US"><span style="font-family: Times New Roman;">XML</span></span><span>数据流),而另一些比较个性的步骤,则由它的继承类自己去完成(如上例中的“形成工厂”,由于各个工厂各不一样,因此由各自的继承类自己去决定它的工厂是怎样形成的)。</span></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 27pt;"><span lang="EN-US"><span style="font-size: small; font-family: Times New Roman;"> </span></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"> </p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span lang="EN-US"><span style="font-size: small; font-family: Times New Roman;"><br><img src="http://dl.iteye.com/upload/attachment/189866/5dd2ee6c-b084-3522-98be-61be888a95f2.jpg" alt=""><br> </span></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 27pt;"><span style="font-size: small;"><span>各个继承类可以根据自己的需要,通过重载重新定义各个步骤函数。但是,外观模式要求不能重载主函数,因此正规的外观模式其主函数应当是</span><span lang="EN-US"><span style="font-family: Times New Roman;">final</span></span><span>(虽然我们常常不这么写)。另外,外观模式还允许你定义的这个步骤中,有些步骤是可选步骤。对与可选步骤,我们通常称为“钩子(</span><span lang="EN-US"><span style="font-family: Times New Roman;">hood</span></span><span>)”。它在编写时,在抽象类中并不是一个抽象函数,但却是一个什么都不写的空函数。继承类在编写时,如果需要这个步骤则重载这个函数,否则就什么也不写,进而在执行的时候也如同什么都没有执行。</span></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 27pt;"><span><span style="font-size: small;">通过以上对外观模式的描述可以发现,外观模式可以大大地提高我们的代码复用程度。</span></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 27pt;"> </p>
<p><span>以上一些常用设计模式,都能使我们快速提高代码质量。还是那句话,设计模式不是什么高深的东西,恰恰相反,它是初学者快速提高的捷径。然而,如果说提高代码复用是提高代码质量的初阶,使用设计模式也只能是提高代码质量的中阶。那么,什么是高阶呢?我认为是那些分析设计理论,更具体地说,就是职责驱动设计和领域驱动设计。</span></p>
<p> </p>
<p> </p>
</div>
<p> </p>
<p> 楼主很强大,不过这个貌似是模板方法模式吧</p>
<div class="quote_div">
<p><span>然而,如果说提高代码复用是提高代码质量的初阶,使用设计模式也只能是提高代码质量的中阶。那么,什么是高阶呢?我认为是那些分析设计理论,更具体地说,就是职责驱动设计和领域驱动设计。</span></p>
</div>
<p> </p>
<p> 从初阶到中阶,这一般是普通程序员工作之后的事,但往往有了工作了,就不思进取了,还停留在初阶甚至之前。</p>
<p> </p>
<p> 这一点我想在国内(外)很普遍吧!</p>
<p> </p>
<p> 都看看楼主的帖,大家都进步一些,工作该多开心啊!</p>
不用管他们的天天变来变去的需求,因为那帮人自己都不懂自己要干嘛,替他们想明白他们要干嘛,他们以后准备拿这个做什么,他们会怎么使用,然后再设计,质量都不差。
不错,我们要避免过度设计,这也是我在项目中不断强调的内容。但是,对需求变更的提前预测是你必须考虑的内容,如果不这样,当项目变更来临时你必然会猝不及防。过度设计与预测变更是一对矛盾的两端,关键在于把握一个度。
怎样才能应对来自客户需求的变化,我认为在于设计,更加精妙的设计。糟糕的设计,只能让你在需求变化的汪洋中望洋兴叹。怎样才能精妙设计呢?答案就在DDD,领域驱动设计。我这篇文章从简到难一步一步描述,最终的归宿就是DDD。
在我的项目中,项目到今年已经是第五个年头了,每年都在维护、变更、增加新功能、升级。在这个项目里,变更和升级是一种常态。相信像这样的项目不在少数。
我经手的一两个项目,一个企业项目,一个国外项目,需求变化也很频繁。其中一个项目,在总共200天的时间内,经历了大小50多次的变化。这其中固然有管理和沟通上的原因,但仔细分析一下,就能发现,不管什么项目,需求的变化是不可避免的!
我们程序员是根据文档写代码的,但代码中的细节肯定比文档中的多得多,这就导致了代码不可能完全符合文档的意图。另一方面,文档也不可能完整地反映客户脑子里的那个实际构想,这就导致了代码(程序)与客户意图的不一致,这就是需求变化的根源。
不要相信“需求挖掘”,“需求分析”可以把客户的意图完整地找出来。假如客户的思维可以细致到代码级别(详细到每一个函数),那么一定会有一种机器,可以把客户的脑波直接编译成他想要的可执行程序!
楼主提到的各种设计模式,实际上是把需求抽象成代码结构的技术。它确实提高了代码的可伸缩性,但它与“适应需求变化”是两码事。如果客户说“使用XML作为通信格式”,那么你当然不应该把XML作为直接处理的数据,而是应该做一层抽象。这就好比设计一个“温度监控程序”,客户说“当温度低于10度时报警”,你当然不应该把“10度”写死到程序中。
那么怎么样才能“灵活适应需求变化”呢?我的意见是不要在这方面做过多的希望。就算你把客户调研得再透彻,跟着他们吃、住、工作,写了一份详细的文档,客户还是会提出他们认为“锦上添花”,但让你猝不及防的变更(客户的想象力有时候太丰富了)。所以,不要去妄想猜测需求今后的变化,这属于“过度设计(Over Design)”。试想,在某一个节点上,有两种以上的变化方向,你是猜对的几率大,还是猜错的几率大呢?
我认为,我们必须接受“需求会变更”的事实,客户方也不得不承担需求变更所带来的影响。程序员只需依据合约中确认过的文档写代码。在这样的情况下,什么才是“高质量的代码”呢?
其实很简单:有完整的单元测试和集成测试的代码,才是高质量的代码。自己写过测试的人就知道,写测试程序时,自己是以“消费者”的身份来使用自己的代码。你的代码写得混乱,那么你的测试程序就很难写,所以你会被迫地去重构,直到你能完整地写出测试程序。这对管理者来说,也是最好的办法:你不能要求你所有的程序员都是经验丰富的高手,但你可以要求他们必须写测试!
有了单元测试和集成测试,你就不用担心代码结构不好了,也不用担心各种需求变化会搞垮程序了。不管代码有多大的变更,跑一跑测试程序,通过了,就OK了。
其实,写"高质量"的代码是很简单的事吧
这是我的专栏,还在继续。。。
发表评论
-
谈谈领域模型的那些事儿 之 从领域获取知识
2012-01-04 12:40 7402前言:你写过用例模型 ... -
如何提高代码质量(管理篇):代码复查
2010-02-22 12:09 9151也许你是一位项目经理,也许你是一位项目骨干成员,或者开发小组长 ... -
一堂如何提高代码质量的培训课 之 领域驱动设计
2010-01-11 23:33 4650终于到了该说说领域驱动设计的时候了。我们在这场关于代码质量的讨 ... -
一堂如何提高代码质量的培训课(3)
2010-01-07 13:37 25823)职责驱动设计和领域 ... -
一堂如何提高代码质量的培训课
2010-01-07 13:02 5561今天这堂培训课讲什么 ... -
谈谈分析模型的那些事儿 之 职责驱动设计
2009-10-16 11:14 3821前面讲了为什么我们要 ... -
谈谈分析模型的那些事儿 之 开始分析
2009-10-16 11:08 3912——对分析模型的一点 ... -
谈谈领域模型的那些事儿 之 注意什么
2009-10-13 01:35 6286前面我们讲了如何从业 ... -
谈谈用例模型的那些事儿 之 注意什么
2009-10-13 00:13 4367前面我们讲了如何建立用例模型,那么建立用例模型应当注意什么呢? ... -
谈谈用例模型的那些事儿 之 用例图
2009-10-12 21:50 5890——对用例模型及其应 ... -
谈谈软件开发的那些事儿 之 解决之道
2009-10-12 21:25 3362前面提出了软件开发的 ... -
谈谈软件开发的那些事儿 之 软件开发的轮回
2009-10-12 21:18 3622——对软件分析设计的一次深刻反思与探讨 前言: ... -
使用软件开发平台的一点儿建议(续)
2008-11-06 10:23 22642. 复用性 (上接)在软件开发过程中,其实很多要实现的功 ... -
设计模式GRASP和GoF是怎样解决耦合的问题
2007-05-14 09:34 5708最近网友Uranus问我了 ... -
一个对象撕心裂肺的怒吼:谁来创建我! GRAPS(4)创建者模式
2007-01-30 14:58 5502当我们分析清楚客户需求设计出用例模型以后,当我们分析清楚客 ... -
(原创)一个优秀软件开发人员的必修课:GRASP(3)高内聚
2007-01-23 14:26 5338在上一章《(原创)一 ... -
(原创)一个优秀软件开发人员的必修课:GRASP(2)低耦合
2007-01-22 14:51 8666我偶然在google或yahoo这样的搜索引擎搜索GRASP发 ... -
(原创)一个优秀软件开发人员的必修课:GRASP软件开发模式浅析
2007-01-19 14:11 14012你是一个优秀软件开发人员吗?你知道GRASP吗?GRASP ...
相关推荐
linux基础进阶笔记,配套视频:https://www.bilibili.com/list/474327672?sid=4493093&spm_id_from=333.999.0.0&desc=1
IMG20241115211541.jpg
GEE训练教程——Landsat5、8和Sentinel-2、DEM和各2哦想指数下载
该资源内项目源码是个人的课程设计、毕业设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过严格测试运行成功才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。
基于springboot家政预约平台源码数据库文档.zip
Ucharts添加stack和折线图line的混合图
基于springboot员工在线餐饮管理系统源码数据库文档.zip
新能源汽车进出口数据 1、时间跨度:2018-2020年 2、指标说明:包含如下指标的进出口数据:混合动力客车(10座及以上)、纯电动客车(10座及以上)、非插电式混合动力乘用车、插电式混合动力乘用车、纯电动乘用车 二、新能源汽车进出口月销售数据(分地区、分类型、分 级别) 1、数据来源:见资料内说明 2、时间跨度:2014年1月-2021年5月 4、指标说明: 包含如下指标 2015年1月-2021年5月新能源乘用车终端月度销量(分类型)部分内容如下: 新能源乘用车(单月值、累计值 )、插电式混合动力 月度销量合计(狭义乘用车轿车、SUV、MPV、交叉型乘用车); 月度销量同比增速(狭义乘用车轿车、SUV、MPV、交叉型乘用车); 累计销量合计(狭义乘用车轿车、SUV、IPV、交叉型乘用车); 累计销量同比增速(狭义乘用车轿车、SUV、MPV、交叉型乘用车); 累计结构变化(狭义乘用车轿车、SUV、IPV、交叉型乘用车); 2015年1月-2021年5月新能源乘用车终端月度销量(分地区)内容如下: 更多见资源内
中心主题-241121215200.pdf
内容概要:本文档提供了多个蓝奏云下载链接及其对应解压密码,帮助用户快速获取所需文件。 适合人群:需要从蓝奏云下载文件的互联网用户。 使用场景及目标:方便地记录并分享蓝奏云上文件的下载地址和密码,提高下载效率。 阅读建议:直接查看并使用提供的链接和密码即可。若遇到失效情况,请尝试联系上传者确认更新后的链接。
基于Java web 实现的仓库管理系统源码,适用于初学者了解Java web的开发过程以及仓库管理系统的实现。
资源名称:Python-文件重命名-自定义添加文字-重命名 类型:windows—exe可执行工具 环境:Windows10或以上系统 功能: 1、点击按钮 "源原文"【浏览】表示:选择重命名的文件夹 2、点击按钮 "保存文件夹"【浏览】表示:保存的路径(为了方便可选择保存在 源文件中 ) 3、功能①:在【头部】添加自定义文字 4、功能②:在【尾部】添加自定义文字 5、功能③:输入源字符 ;输入替换字符 可以将源文件中的字符替换自定义的 6、功能④:自动加上编号_1 _2 _3 优点: 1、非常快的速度! 2、已打包—双击即用!无需安装! 3、自带GUI界面方便使用!
JDK8安装包
配合作者 一同使用 作者地址没有次下载路径 https://blog.csdn.net/weixin_52372189/article/details/127471149?fromshare=blogdetail&sharetype=blogdetail&sharerId=127471149&sharerefer=PC&sharesource=weixin_45375332&sharefrom=from_link
GEE训练教程
该资源内项目源码是个人的课程设计、毕业设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过严格测试运行成功才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。
基于springboot交通感知与车路协同系统源码数据库文档.zip
基于springboot+vue 雅妮电影票购买系统源码数据库文档.zip
为了更好地理解 HTML5 的拖放功能,我们设计了一个简单有趣的示例:将水果从水果区拖放到购物笼中,实时更新数量和价格,并在所有水果被成功放置后,播放音效并显示提示。
该资源内项目源码是个人的课程设计、毕业设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过严格测试运行成功才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。