重拾面向对象软件设计

简介:从上个世纪五十年代冯·诺依曼创造第一台计算机开始,一直到现在只有短短70年时间,从第一门计算机语言FORTRAN,到现在我们常用的C++,JAVA,PYTHON等,计算机语言的演进速度远超我们所使用的任何一门自然语言。从最早的面向机器,再到面向过程,到演化为现在我们所使用的面向对象。不变的是编程的宗旨,变化的是编程的思想。

image.png

作者 | 聂晓龙
来源 | 阿里技术公众号

你还在用面向对象的语言,写着面向过程的代码吗?

一 前言

在欧洲文艺复兴时期,一位伟大的数学家天文学家-哥白尼,在当时提出了日心说,驳斥了以地球为宇宙中心的天体思想,由于思想极其超前,直到半个世纪后开普勒伽利略等人经过后期研究,才逐步认可并确立了当时哥白尼思想的先进性。

无独有偶,在软件工程领域也上演着同样的故事。半个世纪前 Kristen Nygaard发明了Simula语言,这也是现在被认同的世界上第一个明确实现面向对象编程的语言,他提出了基于类的编程风格,确定了"万物皆对象"这一面向对象理论的"终极思想",但在当时同样未受到认可。Peter Norvig 在 Design Patterns in Dynamic Programming 对此予以了驳斥,并表述我们并不需要什么面向对象。半个世纪后 Robert C.Martin、Bertrand Meyer、Martin Fowler等人,再次印证并升华了面向对象的设计理念。编程思想的演进也不是一蹴而就,但在这一个世纪得到了飞速的发展。

二 编程思想的演进

从上个世纪五十年代冯·诺依曼创造第一台计算机开始,一直到现在只有短短70年时间,从第一门计算机语言FORTRAN,到现在我们常用的C++,JAVA,PYTHON等,计算机语言的演进速度远超我们所使用的任何一门自然语言。从最早的面向机器,再到面向过程,到演化为现在我们所使用的面向对象。不变的是编程的宗旨,变化的是编程的思想。

1 面向机器

image.png

计算机是01的世界,最早的程序就是通过这种01机器码来控制计算机的,比如0000代表读取,0001代表保存等。理论上这才是世界上最快的语言,无需翻译直接运行。但弊端也很明显,那就是几乎无法维护。运行5毫秒,编程3小时。由于机器码无法维护,人们在此基础上发明了汇编语言,READ代表0000,SAVE代表0001,这样更易理解和维护。虽然汇编在机器码上更可视更直观,但本质上还是一门面向机器的语言,依然还是存在很高的编程成本。

2 面向过程

image.png

面向过程是一种以事件为中心的编程思想,相比于面向机器的编程方式,是一种巨大的进步。我们不用再关注机器指令,而是聚焦于具体的问题。它将一件事情拆分成若干个执行的步骤,然后通过函数实现每一个环节,最终串联起来完成软件设计。

流程化的设计让编码更加清晰,相比于机器码或汇编,开发效率得到了极大改善,包括现在仍然有很多场景更适合面向过程来完成。但软件工程最大的成本在于维护,由于面向过程更多聚焦于问题的解决而非领域的设计,代码的重用性与扩展性弊端逐步彰显出来,随着业务逻辑越来越复杂,软件的复杂性也变得越来越不可控。

3 面向对象

image.png

面向对象以分类的方式进行思考和解决问题,面向对象的核心是抽象思维。通过抽象提取共性,通过封装收敛逻辑,通过多态实现扩展。面向对象的思想本质是将数据与行为做结合,数据与行为的载体称之为对象,而对象要负责的是定义职责的边界。面向过程简单快捷,在处理简单的业务系统时,面向对象的效果其实并不如面向过程。但在复杂系统的设计上,通用性的业务流程,个性化的差异点,原子化的功能组件等等,更适合面向对象的编程模式。

但面向对象也不是银弹,甚至有些场景用比不用还糟,一切的根源就是抽象。根据 MECE法则 将一个事物进行分类,if else 是软件工程最严谨的分类。我们在设计抽象进行分类时,不一定能抓住最合适的切入点,错误的抽象比没有抽象复杂度更高。里氏替换原则的创始人Barbara Liskov 谈抽象的力量 The Power of Abstraction。

三 面向领域设计

1 真在“面向对象”吗

// 捡入客户到销售私海
public String pick(String salesId, String customerId){// 校验是否销售角色Operator operator = dao.find("db_operator", salesId);if("SALES".equals(operator.getRole())){return "operator not sales";}// 校验销售库容是否已满int hold = dao.find("sales_hold", salesId);List<CustomerVo> customers = dao.find("db_sales_customer", salesId);if(customers.size() >= hold){return "hold is full";}// 校验是否客户可捡入Opportunity opp = dao.find("db_opportunity", customerId);if(opp.getOwnerId() != null){return "can not pick other's customer";}// 捡入客户opp.setOwnerId(salesId);dao.save(opp);return "success";
}

这是一段CRM领域销售捡入客户的业务代码。这是我们熟悉的Java-面向对象语言,但这是一段面向对象代码吗?完全面向事件,没有封装没有抽象,难以复用不易扩展。相信在我们代码库,这样的代码不在少数。为什么?因为它将成本放到了未来。我们将此称之为“披着面向对象的外衣,干着面向过程的勾当。”

在系统设计的早期,业务规则不复杂,逻辑复用与扩展体现得也并不强烈,而面向过程的代码在支撑这些相对简单的业务场景是非常容易的。但软件工程最大的成本在于维护,当系统足够复杂时,当初那些写起来最easy的代码,将来就是维护起来最hard的债务。

2 领域驱动设计

image.png

还有一种方式我们也可以这么来写,新增“商机”模型,通过商机来关联客户与销售之间的关系。而商机的归属也分为公海、私海等具体归属场景。商机除了有必要的数据外,还应该收拢一些业务行为,捡入、开放、分发等。通过领域建模,利用面向对象的特性,确定边界、抽象封装、行为收拢,对业务分而治之。

当我们业务上说“商机分发到私海”,而我们代码则是“opportunity.pickTo(privateSea)”。这是领域驱动所带来的改变,面向领域设计,面向对象编程,领域模型的抽象就是对现实世界的描述。但这并非一蹴而就的过程,当你只触碰到大象的身板时,你认为这是一扇门,当你触碰到大象的耳朵时,你认为是一片芭蕉。只有我们不断抽象不断重构,我们才能愈发接近业务的真实模型。

Use the model as the backbone of a language, Recognize that a change in the language is a change to the model.Then refactor the code, renaming classes, methods, and modules to conform to the new model
--- Eric Evans 《Domain-Driven Design Reference》
译:使用模型作为语言的支柱,意识到言语的改变就是对模型的改变,然后重构代码,重命名类,方法和模块以符合新模型。

3 软件的复杂度

image.png

这是Martin Flowler在 Patterns of Enterprise Application Architecture 这本书中所提的关于复杂度的观点,他将软件开发分为数据驱动与领域驱动。很多时候开发的方式大家倾向于,拿到需求后看表怎么设计,然后看代码怎么写,这其实也是面向过程的一个表现。在软件初期,这样的方式复杂度是很低的,没有复用没有扩展,一人吃饱全家不饿。但随着业务的发展系统的演进,复杂度会陡增。

而一开始通过领域建模方式,以面向对象思维进行软件设计,复杂度的上升可以得到很好的控制。先思考我们领域模型的设计,这是我们业务系统的核心,再逐步外延,到接口到缓存到数据库。但领域的边界,模型的抽象,从刚开始成本是高于数据驱动的。

The goal of software architecture is to minimize the human resources required to build and maintain the required system.
--- Robert C. Martin 《Clean Architecture》
译:软件架构的终极目标是,用最小的人力成本来满足构建和维护该系统的需求

如果刚开始我们直接以数据驱动面向过程的流程式代码,可以很轻松的解决问题,并且之后也不会面向更复杂的场景与业务,那这套模式就是最适合这套系统的架构设计。如果我们的系统会随着业务的发展逐渐复杂,每一次的发布都会提升下一次发布的成本,那么我们应该考虑投入必要的成本来面向领域驱动设计。

四 抽象的品质

抽象永远是软件工程领域最难的命题,因为它没有规则,没有标准,甚至没有对错,只分好坏,只分是否适合。同样一份淘宝商品模型的领域抽象,可以算是业界标杆了,但它并非适合你的系统。那我们该如何驾驭“抽象”呢?UML的创始人Grady booch在 Object Oriented Analysis and Design with Applications 一书中,提到了评判一种抽象的品质可以通过如下5个指标进行测量:耦合性、内聚性、充分性、完整性与基础性。

1 耦合性

一个模块与另一个模块之间建立起来的关联强度的测量称之为耦合性。一个模块与其他模块高度相关,那它就难以独立得被理解、变化或修改。TCL语言发明者John Ousterhout教授也有同样的观点。我们应该尽可能减少模块间的耦合依赖,从而降低复杂度。

Complexity is caused by two things: dependencies and obscurity.
--- John Ousterhout 《A Philosophy of Software Design》
译:复杂性是由两件事引起的:依赖性和模糊性。

但这并不意味着我们就不需要耦合。软件设计是朝着扩展性与复用性发展的,继承天然就是强耦合,但它为我们提供了软件系统的复用能力。如同摩擦力一般,起初以为它阻碍了我们前进的步伐,实则没有摩擦力,我们寸步难行。

2 内聚性

内聚性与耦合性都是结构化设计中的概念,内聚性测量的是单个模块里,各个元素的的联系程度。高内聚低耦合,是写在教科书里的观点,但我们也并非何时何地都应该盲目追求高内聚。

内聚性分为偶然性内聚与功能性内聚。金鱼与消防栓,我们一样可以因为它们都不会吹口哨,将他们抽象在一起,但很明显我们不该这么干,这就是偶然性内聚。最希望出现的内聚是功能性内聚,即一个类或模式的各元素一同工作,提供某种清晰界定的行为。比如我将消防栓、灭火器、探测仪等内聚在一起,他们是都属于消防设施,这是功能性内聚。

3 充分性

充分性指一个类或模块需要应该记录某个抽象足够多的特征,否则组件将变得不用。比如Set集合类,如果我们只有remove、get却没有add,那这个类一定没法用了,因为它没有形成一个闭环 。不过这种情况相对出现较少,只要当我们真正去使用,完成它的一系列流程操作后,缺失的一些内容是比较容易发现并解决的。

4 完整性

完整性指类或模块需要记录某个抽象全部有意义的特征。完整性与充分性相对,充分性是模块的最小内涵,完整性则是模块的最大外延。我们走完一个流程,可以清晰得知道我们缺哪些,可以让我们马上补齐抽象的充分性,但可能在另一个场景这些特征就又不够了,我们需要考虑模块还需要具备哪些特征或者他应该还补齐哪些能力。

5 基础性

充分性、完整性与基础性可以说是3个相互辅助相互制约的原则。基础性指抽象底层表现形式最有效的基础性操作(似乎用自己在解释自己)。比如Set中的add操作,是一个基础性操作,在已经存在add的情况下,我们是否需要一次性添加2个元素的add2操作?很明显我们不需要,因为我们可以通过调用2次add来完成,所以add2并不符合基础性。

但我们试想另一个场景,如果要判断一个元素是否在Set集合中,我们是否需要增加一个contains方法。Set已经有foreach、get等操作了,按照基础性理论,我们也可以把所有的元素遍历一遍,然后看该元素是否包含其中。但基础性有一个关键词叫“有效”,虽然我们可以通过一些基础操作进行组合,但它会消耗大量资源或者复杂度,那它也可以作为基础操作的一个候选者。

五 软件设计原则

抽象的品质可以指导我们抽象与建模,但总归还是不够具象,在此基础上一些更落地更易执行的设计原则涌现出来,最著名的当属面向对象的五大设计原则 S.O.L.I.D。

1 开闭原则OCP

Software entities should be open for extension,but closed for modification
-- Bertrand Meyer 《Object Oriented Software Construction》
译:软件实体应当对扩展开放,对修改关闭。

开闭原则是Bertrand Meyer 1988年在 Object Oriented Software Construction 书中所提到一个观点,软件实体应该对扩展开放对修改关闭。
我们来看一个关于开闭原则的例子,需要传进来的用户列表,分类型进行二次排序,我们代码可以这样写。

public List<User> sort(List<User> users, Enum type){if(type == AGE){// 按年龄排序users = resortListByAge(users);}else if(type == NAME){// 按名称首字母排序users = resortListByName(users);}else if(type == NAME){// 按客户健康分排序users = resortListByHealth(users);}return users;
}

上述代码就是一个明显违背开闭原则的例子,当我们需要新增一种类似时,需要修改主流程。由于这些方法都定义在私有函数中,我们哪怕对现有逻辑做调整,我们也需要修改到这份代码文件。

还有一种做法,可以实现对扩展开放对修改关闭,JDK的排序其实已经为我们定义了这样的标准。我们将不同的排序方式进行抽象,每种逻辑单独实现,单个调整逻辑不影响其他内容,新增排序方式也无需对已有模块进行调整。

image.png

2 依赖倒置DIP

High level modules shouldnot depend upon low level modules.Both should depend upon abstractions. Abstractions should not depend upon details. Details should depend upon abstractions
--- Robert C.Martin C++ Report 1996
译:高层模块不应该依赖低层模块,两者都应该依赖抽象;抽象不应该依赖细节,细节应该依赖抽象。

Robert C.Martin是《Clean Code》《Code Architecture》两本经典书籍的作者,1996年他在C++ Report中发表了一篇名为 The Dependency Inversion Principle 的文章。他认为模块间的依赖应该是有序的,高层不应该依赖低层,低层应该依赖高层,抽象不应该依赖细节,细节应该依赖抽象。

image.png

怎么理解Robert C.Martin的这一观点。我们看这张图,我们的手可以握住这个杯子,是我们依赖杯子吗?有人说我们需要调杯子提供的hold服务,我们才能握住它,所以是我们依赖杯子。但我们再思考一下,棍子我们是不是也可以握,水壶我们也可以握,但猫狗却不行,为什么?因为我们的杯子是按照我们的手型进行设计的,我们定义了一个可握持的holdable接口,杯子依赖我们的需求进行设计。所以是杯子依赖我们,而非我们依赖杯子。

image.png

依赖倒置原则并非一个新创造的理论,我们生活的很多地方都有在运用。比如一家公司需要设立“法人”,如果这家公司出了问题,监管局就会找公司法人。并非监管局依赖公司提供的法人职位,它可以找到人,而是公司依赖监管局的要求,才设立法人职位。这也是依赖倒置的一种表现。

3 其他设计原则

这里没有一一将 S.O.L.I.D 一一列举完,大家想了解的可以自行查阅。除了SOLID之外,还有一些其他的设计原则,同样也非常优秀。

PLOA最小惊讶原则

If a necessary feature has a high astonishment factor, it may be necessary to redesign the feature
-- Michael F. Cowlishaw
译:如果必要的特征具有较高的惊人因素,则可能需要重新设计该特征。

PLOA最小惊讶原则是斯坦福大家计算机教授 Michael F. Cowlishaw 提出的。不管你的代码有“多好”,如果大部分人都对此感到吃惊,或许我们应该重新设计它。JDK中就存在一例违反PLOA原则的案例,我们来看下面这段代码。

image.png

在分享会上,我故意将这行注释遮盖起来,大家都猜不到 newFormatter.getClass() 这句代码写在这里的作用。如果要检查空指针,完全可以用Objects工具类提供的方法,实现完全一样,但代码表现出来的含义就千差万别了。

image.png

KISS简单原则

Keep it Simple and Stupid
-- Robert S. Kaplan
译:保持愚蠢,保持简单

KISS原则是 Robert S. Kaplan 提出的一个理论,Kaplan并非是一个软件学家,他是平衡积分卡Balanced Scorecard创始人,而他所提出的这个理论对软件行业依然适用。把事情变复杂很简单,把事情变简单很复杂。我们需要尽量让复杂的问题简明化、简单化。

六 写在最后

软件设计的最大目标,就是降低复杂性,万物不为我所有,但万物皆为我用。引用JDK集合框架创办人Josh Bloch 的一句话来结束。学习编程艺术首先要学会基本的规则,然后才能知道什么时候可以打破这些规则。

You should not slavishly follow these rules, but violate them only occasionally and with good reason. Learning the art of programming, like most other disciplines, consists of first learning the rules and then learning when to break them.
--- Josh Bloch 《Effective Java》
译:你不该盲目的遵从这些规则,应该只在偶尔情况下,有充分理由后才去打破这些规则

学习编程艺术首先要学会基本的规则,然后才能知道什么时候可以打破这些规则

原文链接
本文为阿里云原创内容,未经允许不得转载。

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

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

相关文章

数据与AI如何提升IT运维价值?锐捷发布乐享智能运维管理平台

运维的价值如何体现&#xff0c;听刚刚发布锐捷乐享智能运维管理平台的锐捷网络怎么说。 编辑 | 宋慧 出品 | CSDN云计算 运维可能是 IT 技术人中最苦的岗位。 IT系统运维工作就像安全一样&#xff0c;平稳运行不出事故是“份内事”&#xff0c;出了事故则要冲到修复一线。因此…

阿里云服务网格ASM集成SLS告警

简介&#xff1a;随着微服务的流行&#xff0c;微服务的架构也在不断的发展演进&#xff0c;Spring Cloud 与 Dubbo为代表的微服务开发框架也得到了普及和落地&#xff1b;在云原生时代&#xff0c;无侵入的服务网格&#xff08;Service Mesh&#xff09;开始走向成熟&#xff…

走进英特尔中国研究院探索创新日:发布最新科研成果与创新策略

如何穿越技术周期&#xff0c;英特尔揭秘创新策略与最新科研进展。 “2022英特尔中国研究院探索创新日”在北京举行。此次活动以“智探索汇无界”为主题&#xff0c;展示了英特尔中国研究院一系列前瞻性研究项目和创新成果。活动中&#xff0c;英特尔研究院副总裁、英特尔中国研…

零基础玩转SLS日志查询-SLS Query Builder发布

简介&#xff1a;日志服务&#xff08;Log Service&#xff0c;简称 SLS&#xff09; 是阿里云提供的行业领先的日志大数据解决方案&#xff0c;一站式提供数据收集、清洗、分析、可视化、告警等功能。智能查询分析是数据中台重要的一环&#xff0c;SLS支持秒级查询10亿到千亿级…

块存储监控与服务压测调优利器-EBS Lens发布

简介&#xff1a;SLS团队联合EBS团队发布了EBS Lens&#xff0c;针对块存储提供数据分析、资源监控的功能&#xff0c;可以帮助用户获取云上块存储资源信息与性能监控数据、提升云上块存储资源的管理效率、高效分析业务波动与资源性能消耗情况。 EBS监控现状 块存储是阿里云为…

阿里云 EventBridge 事件驱动架构实践

简介&#xff1a;我们认为 EventBridge 是云原生时代新的计算驱动力&#xff0c;这些数据可以驱动云的计算能力&#xff0c;创造更多业务价值。 作者&#xff1a;周新宇 本文内容整理自 中国开源年会 演讲 首先做一个自我介绍&#xff0c;我是 RocketMQ 的 PMC member 周新宇…

代理网关设计与实现(基于NETTY)

简介&#xff1a;本文重点在代理网关本身的设计与实现&#xff0c;而非代理资源的管理与维护。 作者 | 新然 来源 | 阿里技术公众号 一 问题背景 平台端购置一批裸代理&#xff0c;来做广告异地展现审核。从外部购置的代理&#xff0c;使用方式为&#xff1a;通过给定的HTTP …

Gartner 发布2022年数据分析十二大趋势:数据和分析将成为创新起源

作者 | 宋慧出品 | CSDN 云计算数据与分析将会成为创新的起源&#xff0c;为企业发展创新助力同时&#xff0c;还将有基于数据的变现、降本增效&#xff0c;基于数据与分析的决策能力已成为有韧性企业的最核心能力。对数据的分析已经被企业与技术界愈加重视&#xff0c;国际研究…

“2021ISIG中国产业智能大会低代码峰会”即将开幕,钉钉宜搭叶周全受邀出席

简介&#xff1a;2021年12月8-9日&#xff0c;“2021ISIG中国产业智能大会” 将在上海举行。阿里巴巴资深技术专家&#xff0c;钉钉宜搭创始人叶周全将作为特邀嘉宾出席大会。 2021年12月8-9日&#xff0c;由中国电子技术标准化研究院、苏州市金融科技协会、中国计算机用户协会…

在 react 里写 vue3 ? 还写了自定义 hooks和 Hoc 构建了响应式 !

作者 | &#x1f47d;来源 | 前端Sharing前言自从vue3.0正式发布之后&#xff0c;vue3.0核心响应式部分被单独抽离成vue/reactivity包&#xff0c;也就是说&#xff0c;我们可以脱离vue框架之外&#xff0c;单独使用vue/reactivity做一些其他的愉快的事&#x1f60a;&#xff0…

阿里云云效发布研发协同工具,以新的产研协同工作方式助力实现BizDevOps

简介&#xff1a;2021云栖大会云效BizDevOps分论坛上&#xff0c;阿里云云效技术负责人陈鑫发布阿里云云效产品研发协同工具支撑ALPD理论&#xff0c;以新的产研协同工作方式助力实现BizDevOps。 编者按&#xff1a;10月21日&#xff0c;2021云栖大会云效BizDevOps分论坛上&am…

打通JAVA与内核系列之一ReentrantLock锁的实现原理

简介&#xff1a;写JAVA代码的同学都知道&#xff0c;JAVA里的锁有两大类&#xff0c;一类是synchronized锁&#xff0c;一类是concurrent包里的锁&#xff08;JUC锁&#xff09;。其中synchronized锁是JAVA语言层面提供的能力&#xff0c;在此不展开&#xff0c;本文主要讨论J…

android如何创建spinner组件,Andriod开发之下拉列表控件(Spinner)的用法

Spinner是Android的下拉列表控件&#xff0c;今天对这个控件进行了学习&#xff0c;发现该控件比其它简单控件使用起来稍微复杂&#xff0c;特地将Spinner控件的使用方法以及注意事项记录下来&#xff0c;以备后用。Spinner控件在Android中的继承结构如下&#xff1a;java.lang…

恒生与中国信通院联合发布《证券行业分布式核心系统SRE运维白皮书》

在互联网金融模式的变革和冲击下&#xff0c;金融机构面临着海量客户管理、业务场景快速增长、金融服务和产品多样化等挑战。 为应对不断增加的技术创新需求&#xff0c;证券行业核心系统正逐步从传统IT集约型架构向支持敏捷开发、弹性扩容、智能灵活的分布式架构转型&#xff…

媒体声音 | 阿里云王伟民:阿里云数据库的策略与思考

简介&#xff1a;DTCC 2021大会上&#xff0c;阿里云数据库事业部 产品与解决方案部总经理 王伟民&#xff08;花名&#xff1a;唯敏&#xff09;发表主题演讲《云原生数据库2.0&#xff0c;一站式全链路数据管理与服务》&#xff0c;并接受IT168企业级&ITPUB执行总编 老鱼…

阿里云云治理中心正式上线,助力企业快速云落地

简介&#xff1a;2021年11月1日&#xff0c;阿里云"云治理中心"&#xff08;Cloud Governance Center)产品正式上线&#xff0c;云治理中心是基于企业IT治理的最佳实践&#xff0c;帮助客户快速搭建业务上云的标准Landing Zone&#xff08;上云登陆区&#xff09;&am…

超值一篇分享,Docker:从入门到实战过程全记录

作者 | 天元浪子来源 | CSDN博客和Docker相关的概念想要真正理解Docker&#xff0c;就不得不从虚拟化技术的发展历程说起。普遍认为虚拟化技术经历了物理机时代、虚拟机时代&#xff0c;目前已经进入到了容器化时代。可以说&#xff0c;Docker是虚拟化技术不断发展的必然结果。…

linux phpunit 安装,在CentOS 7/CentOS 8系统中安装PHPUnit的方法

本文介绍在CentOS 7/CentOS 8操作系统中安装PHPUnit的方法&#xff0c;只需要运行几个命令就可以了&#xff0c;非常的简单。PHPUnit是PHP应用程序的单元测试框架&#xff0c;它是单元测试框架的xUnit体系结构的一个实例&#xff0c;它在JUnit中很受欢迎&#xff0c;PHPUnit需要…

解读如何安全快速建立IT治理环境

简介&#xff1a;云计算经过十多年的发展&#xff0c;从基础的IAAS&#xff0c;大数据&#xff0c;到各种的PaaS有丰富的产品和生态&#xff0c;非常有效地助力了业务增长和技术创新&#xff0c;并提高了业务的效率。最直观的感受是过去需要几天到一个月的资源交付&#xff0c;…

com+ system application 启动_dubbo启动引导过程(基于2.7.9)

前言再百度或google上一搜索dubbo服务暴露过程 相关的文章已经有很多了&#xff0c;但是文章基本都是基于老版本的dubbo&#xff0c;当你对着文章去看下载下来的代码时&#xff0c;会发现很多东西对不上&#xff1b;出于此目的&#xff0c;我便有了自己根据新版本&#xff08;就…