面向对象分析与设计的底层逻辑(代码片段)

CSDN云计算 CSDN云计算     2022-12-01     640

关键词:

作者 | 不拔

来源 | 阿里巴巴中间件

面向对象是符合人认识事物的基本方法

01

人是怎么认识事物的

在面向对象出现之前,已有面向过程的分析方法,为什么面向对象被提出了呢?究其本质原因,人们发现面向过程并不是按照人正常认识事物的方式去分析软件,那么人究竟是怎么认识事物的呢,Yourdon 在《面向对象的分析》一书中提到,人类认识事物是遵循分类学的原理,分类学主要包含三点:区分对象及其属性;区分整体对象及其组成部分;不同对象类的形成及区分。

我们现在可以回想下我们认识事物的过程,是不是和分类学所提到的 3 个要点很相似,看到一个事物,大概会感知到它的组成结构是怎样的,形状是怎样的,属于什么分类。所以,人认识事物是以对象的视角切入的,然后赋于对象具体的概念,比如苹果、梨子、汽车等等概念名称。

021

分类与分层的两种思维

我们面对的现实世界是非常复杂的,应对复杂事物的有一个重要的方法即是抽象,抽象在实际应用过程中,又体现在两种方法上:分层和分类。分类即是将有差异的事物归类到不同的分组中,正如我们常听到的"物以类聚、人以群分"的道理一样,产生分类的原因有两点:一点是事物间的关联紧密程度,不需要将所有的事物都耦合在一起;另一点是人掌握事物是有局限的,只能掌握少量的要点,比如 5~7 个要点,超过了容易忘记。


分层是通过不同的视角看事物,每一层的关注点是不一样的,这种关注点不同是由自己的视角造成的,比如我们理解计算机,并不需要深入到二进制电信号去理解计算机。层次特性在软件设计中我们经常遇到,比如计算机体系结构、TCP 七层协议等,层次特性有一个特点:越往上越具体、越往下越抽象,越往上的内容越不稳定,也即是容易变化。

03

问题域到解空间的映射

我们把需要解决的问题称之为问题域,或者问题空间,把解决方案称之为解空间。正向上一小节中提到的事物有层次特性,不同的人理解的事物是站在各自理解的视角,这样大家的理解、沟通并不一致的。如果我们看到的问题空间是表层的,那么基于浅层次理解设计出来的方案就会不稳定,可能下次有一个小变化导致方案需要重新设计。


我们可以把一个软件划分成三层:场景、功能和实体,场景层是经常会变的,比如发放优惠券场景就非常多,比如有天降红包领取优惠、分享有礼领取优惠券、新人注册领取优惠券等,这种场景的更迭随着业务的调整变化得非常快,因此场景层是不稳定的。功能支撑某一些的场景集合,对比场景,功能相对而言稳定些,就像前面提到的发放优惠券场景,本质就是给用户发放优惠券,只需要提供发放优惠券的功能即可,至于哪些场景来调用它并不关注,但功能还是基于场景的集合抽象出来的,如果场景场景类型变化了,功能也就随之变化,比如担保交易和预售交易就不一样。实体是稳定的,以担保交易和预售交易为例,它的订单模型大致是一样的,只是新增加了一些信息而已。


因此,我们希望从问题空间到解空间,大家看到的、理解的是一致的,而且看到的是问题的本质而非表象,往往场景、功能是不稳定的,而面向过程又是以功能驱动的,所以在易变化的场景下,它面临的问题就比较多。比较稳定的是问题空间中的实体对象,所以面向对象分析是现实的需要。面向过程和面向对象是两个不同的视角的分析方法:面向过程是一种归纳的分析方法,由外到内的过程;面向对象是一种演绎的分析方法,由内到外的过程。

04

三个一致性

软件开发会经历需要分析、概要设计、详细设计、编码、测试、上线主要阶段,我们不希望每块是割裂的,比如分析做完之后,做设计阶段又要重新去做分析的工作,那么这里面就涉及到一致性的问题,即需求到分析的一致性、分析到设计的一致性、设计到编码的一致性。这样做的好处可以保证无信息失真,因此我们急需求一种分析设计方法能做到这一点,面向对象分析与设计就能做到,因此全流程是以对象作为分析与设计的目标,在最终编码中也都是对象。

05

面向对象的底层逻辑

提到面向对象,有部分人会提到封装、继承、多态等特性,然后这些并不是面向对象的本质特性,比如封装,面向过程中也有封装,多态面向过程也有体现,这些特性算不上面向对象特有的特性。面向对象的底层逻辑是基于现实事物做的抽象映射:现实事物对应软件中的对象,我们讨论解空间能对应到问题空间中的对象,两者是一一直接映射的,其它的分析方法是问题空间到解空间的间接映射。


面向对象分析与设计的全景图

01

我们面临的问题是什么

从顶层看,我们要完成需求到编码的工作,然而从需求到编码又会经过多个阶段,如需求分析、方案设计等,从大的层面讲,我们主要遇到三个问题:

1. 做什么的问题

看似这是一个简单的问题,但在复杂的业务场景下,对做什么的理解太重要了,因为不同的人对需求的理解是不同的,比如最近做了一个项目,有一个业务判断规则是只针对跨境订单计税,最开始开发同学的理解是判断卖家类型是否是跨境卖家,然而到了测试阶段,发现大家对这个业务规则判断理解是不一致的,跨境订单跟卖家类型是没有关系的,真正的跨境订单计税场景是 shipTo(收货地址)和 shipFrom(发货地址)国家地址是不一样的。在大项项目中,涉及到多个团队之间的协同,这样的问题异常突出。而且从业务诉求到产品需求,再到技术方案,这其中是经过了 2 次变换,每次变换是不同的角色在里面,大家的认识也会不一样。

2. 怎么做的问题

落实到事情具体要怎么做时,往往大家并不会出大的问题,怎么做偏具体执行阶段,程序员往往在逻辑严密性上没多大的问题,往往出问题是在第一个问题上,相当于方向弄错了,所做的工作也是无用的。

3. 方法指导的问题

我们往往希望不劳而获得到一种万能的方法,能够应对所有的问题,同时又看不起低级的方法,比如大部分人对用例分析方法嗤之以鼻,想要能体现技术水平高大上的方法。其实自上世纪 70、80 年代,软件的分析设计方法并没有太大的变化,而且在我们大学期间都学过,只是大家并不认为它是一种高大上的方法而已。

02

分析到设计的过程

在本节中,我们推导软件分析到设计的过程,由粗到细,最终落实到我们接触到的 UML 知识上。从需求提出到编码实现,这中间有两个关键问题:一是界定目标,即是定义清楚要做什么的问题,相当于是我们做事的方向、目标;二是具体如何做的问题,即通过怎样具体的方案支撑需求目标实现。因此,我们需要一种方法能够帮助我们界定目标和表示具体方案,而且是大家互认的一种通用的方法。

通过用例图可以帮我们界定目标,用例中有三个关键要素:用户、场景和目标。比如交易下单是一个用例,它的用户是买家,场景包含下单成功和下单失败两个场景,用例的目标是买家可以购买心仪的商品。当用例目标确定了,相当于界定了目标,知道需求要做什么,这个过程要反复和业务方确认好,至到最终大家对目标的理解是一致的,方向对了,具体怎么做就好办了。

具体怎么做用时序图表示,画时序图需要注意的一点是顶层的对象层次要一致,不能有的对象表示具体的实体对象,有的表示系统对象,即对象的层级是一致的,要么大家都是系统,比如导购系统调用交易系统,交易系统调用支付系统,要么大家都是对象,比如商品、订单等。通过时序图可以看到一个完整功能的执行步骤,它就包含具体执行的细节,如正常流程、异常流程。


其实在上面有一个问题,在画时序图时要确定好对象,那么这个对象是怎么来的呢?它是由健壮性图分析出来的,它里面有三个关键的对象:一个是边界对象,这个比较好理解,比如UI界面就是边界对象;另一个是控制对象,即是控制业务流程的对象,如下单服务就可以看作是控制对象;实体对象即是问题空间中的业务对象,比如订单。画健壮性图是有规则的,一般是边界对象调用控制对象,控制对象产生实体对象,比如用户下单界面是边界对象,下单服务是控制对象,订单就是实体对象。

寻找对象之路

01

对象从哪里来

在本文第一部分第三小节中已经提到,问题空间到解空间是一一映射,我们讨论解空间中的对象时,其实它映射到问题空间中的对象,而问题空间中的对象主要来源于业务概念、业务规则、关键事件。大部分的对象是显现的,我们通过理解业务能发现,有的对象是隐性的,需要我们持续对业务有更深的理解才能发掘出来。好的对象模型是需要经过多次迭代打磨出来的,并非一次就能设计得十全十美。

02

发现对象的方法

在本文第二部分第二小节中已经提到寻找对象的方法,不过那还只是关键显现的对象,在本节中主要讲述完整对象发现的方法,主要方法分成四个步骤:

1. 通过健壮性图找到关键的实体对象;

2. 通过结构分析方法找出更多的实体对象;

3. 将对象组成有机的对象模型;

4. 最后通过用例走查对象模型是否完备。

这里以一个案例来说明发现对象的过程,案例是用户在下单时,在订单上展示税的金额。首先画出健壮性图,这里的边界对象是下单界面,控制对象有两个,一个是下单服务,另一个是计税服务,实体对象也有两个,一个是计税单,一个是订单。有了计税单和订单这两个实体对象后,接下来通过结构分析方法,分析出更多的对象。

对象都是有结构的,只要我们掌握了对象的结构,基本上就能掌握对象的概貌,因此我们从对象的结构入手,去分析对象内部的结构、对象关联的结构,实质上是从两个维度出发:一是从自身的角度出发,看自己内部还包含了哪些对象,如主订单包含了子订单;另一个是从外部的角度出发,看自己还与哪些对象相关联,如计税单与订单是有关联的。这种找对象的方法我称之为结构分析方法,因为本身结构又是事物本质的一种表达方式,比如化学分子结构决定化学现象。

为了更好地表达出对象的结构,我的一个经验是给对象下好定义,下定义可以从不同的维度,比如功能性维度、价值性维度、目的性维度、结构性维度等,这里可以从结构性的维度去给对象下定义。以计税单为例,可以给它下一个定义:计税单是将订单金额信息转成若干个标的物计税的单据模型,从这个定义中,我们可以看到计税单是与订单有关联关系的,另一个是计税单是包含了若干个标的物,我们可以画出计税单的对象模型。

当对象模型画出来后,后续我们讨论业务基本上围绕这个对象模型去讨论业务问题的,比如商品标的物哪些金额要参与计税、计税金额的计算口径是怎样的,到这里,大家再体会下"问题空间到解空间一一直接映射"这句话,业务上的诉求也无非是哪些订单费用项要计税,计税的逻辑是怎样的,有可能在这个场景下要扣减金本位优惠,在另外一种场景下金本位优惠不需要扣减,基于对象模型与产品、测试同学讨论问题,大家都是处于同一个维度的视角看问题,沟通理解成本会少很多。

对象模型是一种可视化的表达,我们大部分的沟通问题是缺乏显性表达造成的,这句话可以这样理解,也可以那样理解,导致大家理解有偏差,现在用模型的形式沟通问题,很多偏差、歧义就消除了。

03

组织对象结构

当我们分析出一堆的对象后,还需要经过一定的组织,正如前面提到,人对事物理解是有局限的,不能一下子接受太多的事物,因此可以将它们分成一个个小的域,比如商品域、订单域、税务域等,这样当聚集一个问题时,可以只看某个子域里的对象模型即可。

如何分配职责

01

职责是怎么来的

面向对象最难的点有两个:一个是找出对象;另一个是分配职责。UML 把职责定义为"类元的契约或义务",因此职责的划分从本质来讲还是类元本身决定的,比如订单,它要提供订单渲染、订单创建、订单修改、订单查询的义务。

职责分为两类:一类是认知职责;另一类是行为职责。

  • 认知职责包含:

    • 对私有数据封装的认知。

    • 对相关对象的认知。

    • 对其能够导出或计算的事物的认识。

  • 行为职责包含:

    • 自己执行的行为,包括创建对象或计算。

    • 初始化其它对象的动作。

    • 控制或协调其它对象的活动。

02

分配职责的逻辑

上一小节中提到的职责有两类,认知职责是对象自身的认知范围,即它只能基于自身属性完成相应的职责,举一个例子,假如一主多子的订单,要计算总的订单金额,怎么分配职责呢?首先商品只能查到自身价格的信息,它的认识是基于商品 price 属性,一个子订单可以有多个商品,那么它也只能计算出子订单的金额信息,它的认知是基于 item 和 quantity两个属性,主订单包含所有子订单的信息,那么就可以计算出总的订单金额。

从上面的例子中我们可以看出,认知职责是基于对象属性的,正所谓"不在其位、不谋其政",认知职责一定不会超过它的认识范围的。

行为职责是偏领域服务的,有的时候一个职责不属于某一个对象,比如转账,就是一个行为,让其它的职责承担并不合适,这类行为职责往往是一个显著的业务活动,比如订单渲染、订单创建就是行为职责而非认知职责。

分配职责一定要遵循"信息专家"模式,它的含义是将职责分配给具有完成该职责所需要信息的那个类,也即上面提到的认识产生职责。

03

验证职责分配的合理性

我们期望分配的职责满足"高内聚、低耦合",怎么检验呢?我们再回过头来思考职责的定义:类元的契约或义务,换句话讲,职责是满足其它对象来调用的,这个就与我们画时序图的目的是一致的,每次发生一次调用,即意味着其它的对象要提供一个职责出来,因此我们可以在时序图中看对象间的调用频次,如果一个对象被调用得非常频繁,有可能这个对象承担了太多的职责,是不是可以对其拆分,把职责分配一部分出去。因此,对象职责分配并不是一蹴而就的,需要不断审视、检验。

分配职责是要遵循一定的原则,如创建者模式、信息专家模式、纯虚构模式等,这些原则会在下一篇中单独去讲。

案例

01

案例背景

这里举一个例子,说明面向过程和面向对象在分析、编写代码的差异性,计税需要判断是否满足计税规则,比如虚拟商品不计税(手机充值之类)、有些免税地址不计税、小 B 买家也不计税等,因此需要提供一个计税过滤判断逻辑。

02

常规面向过程实现

面向过程的思路很简单,提供一个过滤方法依次处理下面逻辑:过滤虚拟商品计税请求、过滤免税地址计税请求、过滤小 B 买家计税请求。


 
public void filter(List<TaxCalculateRequest> request)


     // 过滤虚拟商品计税请求
     filterVirtualItem(request);


     // 过滤免税地址计税请求(即外岛)
     filterOuterIsland(request);


     // 过滤小B买家计税请求
     filterPurchaseType(reqeust);


03

面向对象实现

面向过程是从过程视角或者是功能视角分析问题,而面向对象是从对象的视角分析问题,过滤计税请求是计税过滤器判断计税请求是否满足计税规则,这里就包含了两个对象:计税过滤器和计税规则,判断是否满足计税要求这个职责应该是在具体的计税规则处理器中,比如是否是小 B 买家等,因此我们可以画出对象模型。

关键代码如下:


 
public abstract class AbstractRuleHandler 


    /**
     * 抽象的业务规则处理
     *
     * @param request
     */
    public abstract void handler(TaxCalculateRequest request);


    /**
     * 构造函数里完成注册
     */
    public AbstractRuleHandler() 
        TaxCaluclateFilter.register(this);
    

总结

在文章中提到,面向对象的底层逻辑是基于现实事物做的抽象映射,重要的不是要面向对象具体技术的使用上,而是分析问题的思维上,这是最难的,它最大的好处是问题空间到解空间是一一直接映射的,请注意是一一直接映射,它意味着我们在讨论方案的时候,完全可以映射到问题空间,如果是间接映射,也就意味着设计的方案后面会面临重新设计的可能性,因为它是基于场景或功能做出的归纳设计,而且是表层的设计。真正掌握了面向对象分析和设计的方法,也体会到其中的益处,对理解业务、方案设计、编码开发都有好处。

往期推荐

热搜!华为 30 岁以下员工仅占 28%,网友:35 岁危机呢?

中国信通院魏博锴:云原生混部标准解读

疯了?黑客公开“25美元入侵星链”法,SpaceX给他钱,还诚邀大家一起来“黑”?

图解 React 的 diff 算法:核心就两个字 —— 复用

点分享

点收藏

点点赞

点在看

当代码遇到数理逻辑——面向对象设计与构造第三章总结(代码片段)

在面向对象课程中的第三章,我尝试了基于JML语言的规格化设计,按照AppRunner中的接口文件实现了Path类和PathContainer,Graph,RailWaySystem迭代类。JML语言是一种规格化语言,完全建立于数理逻辑上,既能够为开发者实现类与方法时提... 查看详情

面向对象的设计(11)

1.面向对象的分析与设计方法与结构化的分析设计方法有什么不同? 答:结构化系统分析方法是采用自项向下,由外到内,逐层分解\'的思想对复杂的系统进行分解化简,从而有效地控制了系统分析每一步的难度,并运用数据... 查看详情

2018-北航-面向对象567次oo作业分析与小结(代码片段)

设计策略及其变化第五次作业-多线程电梯在这次作业一开始的大部分时间,我一直想着怎样设计最为完美,完全使用BlockingQueue,导致交作业前发现设计并不能满足指导书的要求。最后仓皇之中加了一个新的类,既臃肿,又是轮... 查看详情

面向对象和类的介绍(代码片段)

#一、面向对象#面向过程与面向对象#面向过程:过程即解决问题的过程,就是有逻辑顺序,基于该思想写程序。如设计流程图,是一种机械式的思维方式。#优点:复杂的过程流程化,进而简单化#缺点:扩展性差#面向对象:对象... 查看详情

面向对象分析与设计面向对象设计包括哪些内容

一、总述面向对象分析的输入是用户的功能需求,输出是简单的、理性化的分析模型,此阶段的工作更多侧重于如何理解软件的功能需求;面向对象设计的输入是面向对象分析的结果,蔬菜水果最终的、细化后的设计模型,此阶... 查看详情

uml2面向对象分析与设计--面向对象思维(概念面向对象技术的发展历史对象和类面向对象技术的相关原则:抽象封装分解泛化多态分层复用)(代码片段)

文章目录1.UML2面向对象分析与设计学习目标2.面向对象思维2.1学习目标2.2什么是面向对象2.3面向对象技术的发展历史2.4面向对象技术的优势2.4.1便于沟通:在计算机中模拟现实世界的事和物2.4.2稳定:较小的需求变化不会... 查看详情

面向对象(代码片段)

1.三大编程范式面向过程函数式编程面向对象设计2.编程进化论最开始无组织无结构,从简单控制流中按步骤写指令从上述指令中提取重复的代码看或逻辑,组织到一起(定义了一个函数),实现代码重用,由无结构走向了结构... 查看详情

软考面向对象程序设计复习指南(代码片段)

...考纲(1)分析模式与设计模式知识(2)面向对象程序设计知识(3)用C++语言实现常见的设计模式及应用程序。(4)用Java语言实现常见的设计模式及应用程序。2、面向对象基本概念面向对象... 查看详情

java设计模式day1之面向抽象原则:抽象(abstract)类的设计与应用分析(代码片段)

...类非抽象子类2.3、通过上转型对象调用子类方法总结前言面向抽象原则是面向对象四大基本原则的第一条,其重要性不言而喻,面向抽象原则分为抽象(abstract)类和接口(interface 查看详情

面向对象(代码片段)

阅读目录一面向对象的程序设计的由来二什么是面向对象的程序设计及为什么要有它三类与对象四属性查找五绑定到对象的方法的特殊之处六对象之间的交互七练习八继承与派生九多态与多态性十封装十一绑定方法与非绑定方法... 查看详情

面向对象设计与构造课程总结作业(代码片段)

BUAAOO2021THEFINAL面向对象设计与构造课程总结作业四个单元中的架构设计第一单元架构设计与实现相关总结,传送门在此第二单元架构设计与实现相关总结,传送门在此第三单元架构设计与实现相关总结,传送门在此第四单元架构... 查看详情

专题三面向对象(代码片段)

/*一、Java面向对象学习的三条主线:1.Java类及类的成员:属性、方法、构造器;代码块、内部类2.面向对象的三大特征:封装性、继承性、多态性、(抽象性)3.其它关键字:this、super、static、final、abstract、interface、package、import... 查看详情

面向对象和面向对象编程(代码片段)

一、面向过程(一)面向过程的特点1.分析解决步骤,使用函数一步步实现步骤2.以算法为核心3.自顶向下设计,最初就必须对问题深入了解4.大问题——>小问题来求解5.表现形式:使用函数作为划分程序的基本单位6.直接... 查看详情

c++设计模式面向对象设计模式

目录面向对象设计模式理解面向对象的思维模式抽象思维层面理解面向对象面向对象的接口面向对象设计原则一.依赖倒置原则(DIP)二.开放封闭原则(OCP)三.单一职责原则(SRP)四.Liskov替换原则(LSP)五.接口隔离原... 查看详情

面向对象的基本概念(代码片段)

第2章面向对象的基本概念1结构化程序设计1.1了解传统的结构化程序设计的基本方法及其局限性基本方法结构化程序设计也称为面向过程的设计方法,强调数据结构和程序结构,注重代码的易读性,可靠性及可维护性采用自顶向... 查看详情

java设计模式day1之面向抽象原则:抽象类的设计与应用分析(代码片段)

...抽象类非抽象子类2.3、上转型对象调用子类方法总结前言面向抽象原则是面向对象四大基本原则的第一条,其重要性不言而喻,面向抽象原则分为抽象类和接口以及面向抽象编程,由于篇幅有限本文我们主要细说抽象... 查看详情

c++从入门到入土第三篇:类与对象(上篇)(代码片段)

...对象(上篇)文章目录类与对象(上篇)面向过程和面向对象的初步认识类的引入类的访问限定符访问限定符说明类的实例化类对象模型类对象大小的计算this指针面向过程和面向对象的初步认识面向过程(ProcedureOr... 查看详情

面向对象初步(代码片段)

1.注意:Python支持(1)面向过程(2)面向对象(3)函数式编程等多种编程范式2.面向对象编程的思想主要是针对大型软件设计而来的.面向对象编程使得程序的扩展性更强,可读性更好,使得编程可以像搭积木一样简单.3.面向对象编程将数据... 查看详情