面向对象软件设计过时了?(1)

简介: 面向对象软件设计过时了?


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


一 前言


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


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


二 编程思想的演进


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


1 面向机器



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


2 面向过程



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


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


3 面向对象



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


但面向对象也不是银弹,甚至有些场景用比不用还糟,一切的根源就是抽象。根据 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 领域驱动设计



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


当我们业务上说“商机分发到私海”,而我们代码则是“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 软件的复杂度



这是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》

译:软件架构的终极目标是,用最小的人力成本来满足构建和维护该系统的需求


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



相关文章
|
网络协议 网络架构
网络TCP/IP基础(IP地址网络汇总与规划)
网络汇总、汇总方法、汇总案例、要求、过程、其它方法、注意事项、汇总扩展、IP地址规划及应用、IP地址规划的案例、规划原则、规划案例、规划过程、注意事项、IP地址规划的应用、规划应用案例、规划思路、规划应用过程、其它规划
2174 1
网络TCP/IP基础(IP地址网络汇总与规划)
|
物联网
通过微信小程序体验阿里云IoT物联网平台
通过微信小程序体验阿里云IoT物联网平台
10411 0
|
3月前
|
传感器 人工智能 运维
Geo专家于磊:GEO优化里被忽略的关键细节
本文聚焦GEO(生成式引擎优化)中易被忽视却至关重要的十大细节:引用可追溯性、术语锚定、经验显式化、边界正面化、数字口径化、语义覆盖、首句命中、时效标注、人味表达与跨模态信号。普林斯顿等研究证实,落实这些细节可使内容被AI采纳率提升约40%——细节不是修饰,而是决定是否入答案的分水岭。
223 1
|
11月前
|
人工智能 前端开发 搜索推荐
AI聊天居然有17种姿势?提示工程师的武功秘籍大公开
想让ChatGPT更听话?别只会说'请帮我...'了!从零样本到思维树,从检索增强到自动推理,17种提示工程技术让你的AI助手从'憨憨'变'大神'。掌握这些技巧,告别低效对话,让AI真正为你所用!#人工智能 #提示工程 #ChatGPT #大模型
774 11
|
调度 容器 Perl
Nvidia GPU如何在Kubernetes 里工作
# Nvidia GPU如何在Kubernetes 里工作 本文介绍Nvidia GPU设备如何在Kubernetes中管理调度。 整个工作流程分为以下两个方面: * 如何在容器中使用GPU * Kubernetes 如何调度GPU ### 如何在容器中使用GPU 想要在容器中的应用可以操作GPU, 需要实两个目标 1.
4649 0
|
网络协议 Linux Go
[WUSTCTF2020]level4题解
[WUSTCTF2020]level4题解
560 0
[WUSTCTF2020]level4题解
|
机器学习/深度学习 存储 人工智能
达摩院自研向量检索引擎Proxima在行业搜索中的应用
淘宝搜索推荐、视频搜索背后使用了什么样的检索技术?非结构化数据检索,向量检索,以及多模态检索,它们到底解决了什么问题?今天由阿里达摩院的科学家从业务问题出发,抽丝剥茧,深度揭秘达摩院内部技术,向量检索引擎 Proxima,以及在阿里云开放搜索产品行业模板能力的实践应用~
2986 1
达摩院自研向量检索引擎Proxima在行业搜索中的应用
|
存储 运维 监控
能源路由器:基于固态变压器的能源路由器结构与能源流量模型
能源路由器:基于固态变压器的能源路由器结构与能源流量模型
1281 0
能源路由器:基于固态变压器的能源路由器结构与能源流量模型
|
弹性计算 负载均衡 网络协议
设备接入技术交流|学习笔记
快速学习设备接入技术交流。
1324 0
设备接入技术交流|学习笔记

热门文章

最新文章