软件职责是一个老生常谈的话题。如何划分一直都有相关的原则指导进行。可真正能够做到正确,恰当的凤毛麟角。一方面因为每个人对原则的理解不一致,另外一方面不遵守原则也许短期内不会暴露出问题。相反原则的遵守却并不能带来立竿见影的好处,迫于项目进度的压力,原则是可以牺牲的。
关于软件职责的划分原则大到架构设计,小到类,函数的设计书籍连篇累牍,本文不准备堆砌这些知识点。鉴于我们大部分书籍示例都是虚拟项目,本文以真实产品项目中的一个具体问题来阐述职责不合理导致的后果,以及由此延伸出的对软件开发过程中需求分析,架构设计的思考。
为了便于理解后文要阐述的问题。首先对涉及的产品和软件做一个简要介绍。我们事业部核心产品是监护仪,准确来讲是监护系统,包括中央监护站(简称CMS)以及床旁监护仪(简称PM)。床边监护仪的产品形态非常多,这里仅列举本文中涉及的两代监护仪,分别简称为Tx PM,Nx PM。床旁监护仪顾名思义是在床旁直接和病人相连,通过各种传感器采集和计算诸如ECG,SpO2等生理参数,同时监测参数变化并给出报警。中央站通常是部署在护士站的PC监护系统,通过网络与床旁监护仪通信。监护仪将机器状态,生理参数,波形以及报警传输到中央站,实现护士或者医生不需要到各个病床就能掌握病人生理情况的目的。另外中央站上还可以远程对床旁设备进行设置操作。不同时期和团队开发的床旁监护仪和中央站的通信协议不同,上一代的Tx系列采用CMS+协议,而新一代的Nx系列采用MD2协议。
中央站上看到的信息几乎和监护仪界面几乎是一致的。基于业务相同的设计理念,新一代N系列监护系统,中央站和监护仪的软件架构平台"复用"。当然中央站和监护仪所采用的操作系统不同,通过一个内部开发的操作系统适配层(MMOS)进行隔离。由于篇幅有限,MMOS中间层不在本文的论述范围。整个监护系统为"分布式"系统,交互相对复杂。本文探讨的问题涉及其中两个子系统,分别为前端(Frontend)和后端(Backend)。为避免增加阅读和理解问题的难度,同时基于信息安全考虑,文中仅阐述这两个子系统。另外牵涉到上一代Tx监护仪,本文对中央站和其交互过程一并进行呈现。交互框图如下:
Tx系列监护仪不是分布式系统,不区分前端和后端,为单进程程序。Nx系列监护仪的前端与后端作为独立的进程部署,彼此同样采用MD2协议通信。根据架构职责划分原则,后端负责处理参数,波形,报警,机器模式等监护业务逻辑,前端负责呈现参数,波形,报警文字以及声光等。前端采用数据流的架构,核心设计思想是系统中存在一个业务数据模型,会定时周期往UI层推送,UI层响应关注的数据变化刷新显示。
如此长的篇幅来介绍项目背景,只为后续更好的阐述要讨论的问题。开发过程中可能会遇到层出不穷的问题,本文涉及到的不是重大的技术难题,甚至在进度面前都不能称之为问题。但是却非常具有代表性,也侧面反映出软件开发之怪状:简单的事情不按原则办,复杂的事情考虑不全面就开始干。
本文的反思由测试人员提出的故障而引发。故障现象为Nx监护仪连接中央站的情况下,监护仪进入待机模式(中央站和监护仪状态一致,同处于待机模式),此时监护仪因电池电量消耗触发电池电量低的报警,而中央站却没有触发该报警。前文已阐述,中央站和床旁监护仪包括报警在内的信息要保持一致。
当然从临床场景和风险的角度权衡,该问题还没录入故障库就"寿终正寝"了。不过因为测试人员找到我分析故障原因,才有幸看到如下代码:
阐述问题前,有必要澄清下待机及电池电量低报警的需求:
1.待机模式下屏蔽所有报警,包括生理报警和技术报警。
2.待机模式下屏蔽所有报警,包括生理报警和技术报警。[需求变化]电池电量低报警除外。
首先抛开职责划分的原则不谈,单就函数代码可以窥见其开发者对新一代监护软件平台架构是不理解的。新一代架构的核心思想,数据流会定时推送到UI层。该函数仅在响应待机状态变化时清除报警列表无济于事。因为下一秒数据流仍然会推送报警列表到UI层,此时待机状态未变化而不会清除报警列表,因此被清除的报警下一秒又"神奇"的出现在界面上。
从该函数的最后一行代码可以推测应该出现过上面说的问题。该行代码回调的函数实现了进入待机时隐藏报警区。因此避免进入待机时清除报警列表的一秒后,仍然无法屏蔽报警的问题。代码如下:
从功能上讲,此更改修复了故障。但从编程能力上看,该开发者不能写好一个函数。函数职责划分不合理,既然有专门的函数更新报警列表,待机模式下清除报警,理应由该函数处理,而不至于放在响应待机状态变化的函数中处理。也许开发者认为报警列表对象为类的成员变量,放在哪个函数处理都无所谓。但成员变量本质上就是类域的全局变量,具有同样的副作用,访问及改写不受控。造成目前的实现方式,唯一合理的解释是:处理待机状态变化的函数本身就是一个补丁。处理报警列表更新的函数此前已编写完成,可能甚至通过测试。到后期突然测试人员发现某个故障,开发人员因为没有理解之前的代码逻辑或者不愿意更改之前的逻辑,因此新写一个函数来实现清除报警的功能。
退一步讲,打补丁在项目后期无可厚非。但总不能随意,破罐子破摔。既然打补丁就是想在不引入其他问题的情况下,去解决已发现的问题。而在第一次补丁时并没有解决待机下仍显示报警的问题,第二次补丁通过隐藏报警区才解决掉。但其实引入更深层次的问题,极其隐蔽。本文不展开讨论如何打补丁,而是来探讨第二次补丁引入的问题,以及如何避免补丁。其实本文涉及的问题用补丁的方式已无济于事,按下葫芦起了瓢。
在开始探讨接下来的问题之前,首先我对这个补丁函数进行改写,避免两次补丁造成不必要的干扰。重写后的函数如下:
重写的函数和之前的逻辑是等价的,但却可以去掉通知UI待机状态下隐藏报警区的函数,也即是第二次的补丁。可能眼尖的人会发现被去掉的函数,逻辑不对等。if分支隐藏报警区和报警状态区,而else分支却只显示报警状态区,两者不对等。那当退出待机模式后,会不会出现不显示报警区的问题?这里又有一个奇葩做法,惊喜不断,在UI响应报警列表更新的函数中,当报警列表不为空则显示报警区。这个实现方式实在让人难以理解,本来打补丁就是不愿意和之前的更新报警列表函数逻辑有瓜葛,可到最后仍然徒劳,还是在报警列表更新函数增加了显示报警区的逻辑。不过在重写待机状态响应函数之后,去掉了隐藏报警区的逻辑,那么报警列表更新函数中的显示报警区的逻辑就没有存在的必要了。
重写待机状态响应函数后,代码变得相对清晰整洁。实现的逻辑是等价的,但却少一个函数。重写的函数并没有做任何具体的更改,仅仅是根据架构的设计思想来编写代码。从之前函数代码的本质业务逻辑讲,它想干的事情就是待机模式下不显示报警,而至于是否隐藏或者显示报警区是未考虑架构的约束,实现方式不合理导致的额外处理。重写的函数完全实现了之前的本质逻辑,只要在待机模式下就清除报警,而不仅仅是在待机状态发生变化时才清除。这时就会发现隐藏或者显示报警区的处理完全是多此一举。当然重写的函数并不解决任何问题,只为不干扰后续的论述。
从上面的分析可以得出响应待机状态的函数本身是补丁。那么接下来就得分析为什么需要这个补丁。这里有必要推演下床旁监护仪和中央站关于待机模式的需求及开发演变的过程。
上一代的Tx监护仪和中央站为两个团队各自开发。Tx监护仪待机功能的原始需求如前文所述要屏蔽所有报警,但因为Tx监护仪在待机模式下,界面上不显示任何信息,仅显示待机字样提示。而中央站的界面却并不同,除了显示待机字样提示,仍显示报警区。Tx监护仪开发时没有将所有报警在待机模式下都屏蔽干净,而其界面又无法发现该问题。当中央站的测试人员无意中发现Tx监护仪在待机模式下仍有报警发送到中央站,并同时报告给Tx监护仪的开发人员和中央站的开发人员。监护仪开发人员一脸蒙逼,两手一摊说版本都发布了,下个版本再说。中央站的开发人员心里一万只草泥马奔腾,干脆就一不做二不休在中央站屏蔽报警。Tx监护仪下个版本开发过程中因为再没有人反馈待机下未屏蔽报警的问题就不了了之了。
不幸的是,Tx监护仪的待机需求发生变化,考虑到风险,待机模式下不能屏蔽所有报警,至少电池类报警不能屏蔽。这下开发人员炸了锅,首先监护仪待机模式下不显示任何信息,怎么可能显示出报警来?需求人员也跳了起来,态度坚决的说一定要报出来。这么高风险的报警不报,出了问题谁负责。于是大家僵持后不欢而散。
没办法,讨论范围要扩大,中央站开发人员被参与进来。中央站开发人员骂娘了,即使监护仪报出来,中央站不可能报出来,上个版本老子才从根上屏蔽待机下的所有报警,现在来这么一手肯定搞不定。讨论展开拉锯战,突然中央站开发人员提出即使新版本可以再打补丁不屏蔽电池电量低的报警,但是有兼容性问题,监护仪连上个版本还是报不出来。这无疑抛出重磅炸弹,大家一听立刻打起十二分精神。肯定不能这么搞,要换思路,坚决不能引入兼容性问题,不能背这个历史包袱。于是决定得有资深人员来系统性考虑方案,兼顾监护仪和中央站,前提是不能引入兼容性问题。监护仪要支持待机下不屏蔽电池电量低的报警,除了需要更改电源管理模块电池报警触发逻辑外,主界面也需要更改显示出报警区。中央站还需要同步更改再次补丁,屏蔽除电池电量低外的所有报警。但这就会出现前面说的兼容性问题,而且导致监护仪和中央站的业务耦合在一起,更改和测试的工作量也相对较大,这是不可能接受的。
活人终究没被尿憋死,找到了一个"天才"的完美方案。"反向"思维这个时候发挥了至关重要的作用,待机下不屏蔽电池电量低的报警需求既然这么难搞,那为什么不反其道而行之,在出现该报警时自动退出待机模式呢。如此一来,所有问题迎刃而解,Tx监护仪任何时候都可以报出这个报警。而对于中央站而言可以不做任何更改,待机下仍然屏蔽所有报警,监护仪有特殊报警要在待机下报出来就自动退出待机。这是个皆大欢喜的方案,甚至建立起一种处理待机特殊报警的机制,中央站和监护仪待机业务彻底解耦,不需要关注监护仪待机下的特殊处理。而且更改工作量小,影响可控。可唯独需求人员不接受,认为出现电池电量低报警自动退出待机不合适,用户会很困惑。
于是又开会讨论,这次领导和各个技术组的头头都到场。需求人员开始发牢骚抱怨,而开发人员同仇敌忾,认为这是目前最优的方案,不但工作量小,影响可控,而且不会有任何的兼容性问题,说到这开发人员的声音提高了几度。双方各执一词,大家把目光都转向一脸凝重的领导。领导终于开口了,深思熟虑的说这个方案不是最好的方案,但是可行的方案,相比于风险而言,自动退待机用户不至于不接受。另外从临床场景看,待机下不插电源可能性比较小,出现这个问题的概率不大。"绣球"又抛回给需求人员,只见他们敢怒而不敢言,一脸委屈的说这样也行吧。此时领导话锋一转,口气严厉的给在场开发人员训话,我们回去后要反思为什么会出现这样的问题,以后要如何避免类似的问题。所有开发人员若有所思的郑重点点头,一副醍醐灌顶的样子。最后领导做总结呈辞,目前就定这个方案,后面下一代监护系统要从设计上全局考虑,系统性的解决这个问题。
各方向都满意而归,中央站方面不需要进行任何更改,此前那个待机下屏蔽所有报警的补丁就这么根深蒂固的被保留下来。Tx监护仪通过自动退待机"优雅"的规避掉报不出电池电量低报警的问题。监护仪版本顺利发布,大家对这个问题都有较为深刻的印象。
新一代的Nx监护仪在紧锣密鼓的开发中。此前那个待机屏蔽报警的问题刻骨铭心,于是设计时吸取经验教训并"反思",一方面认为Nx监护仪在待机模式时前端界面仍需要显示报警区。另外一方面待机需求又发生变化,退出时必须手动选择解除病人或者恢复监护,不能自动退出。因此只能通过后端屏蔽除电池电量低外的所有报警。Nx监护仪在待机模式下发生电池电量低时,可以正常触发报警并显示,而不在需要自动退出。Nx监护仪前端界面在待机模式可显示报警区,所有的报警都被要求测试在待机模式下被屏蔽而不显示。
待机模式下Nx监护仪后端发送到中央站后端的报警列表已正确处理过,中央站前端只需呈现出来即可。当待机模式下出现电池电量低报警时则显示,不存在时列表为空自然而然就不显示报警。如此中央站前端收到后端的报警列表再进行判断是否为待机模式而清除过滤就画蛇添足了。
由此引发测试人员反馈的Nx监护仪连接中央站待机时出现电池电量低,监护仪可以触发报警,而中央站却不触发报警的问题。前文提到的兼容性问题居然死灰复燃了。出来混迟早要换的,一时的规避,最终又让Nx监护仪再次掉坑里。
出现这个问题时,相关负责人迅速组织会议讨论解决方案。会议的部分参与者经历过此前Tx监护仪和中央站在待机模式屏蔽报警更改方案的讨论。会上有人提出Nx监护仪得按照当年Tx定下的机制实现,待机模式下发生电池电量低的报警时则自动退出。Nx监护仪开发人员强烈反对,这是历史遗留问题,我们新一代产品要甩掉历史包袱,不然负担太重。其实潜台词是Nx监护仪无法自动退出待机。新一代产品跟老产品中央站有点兼容性问题用户也是可以理解的嘛。况且Nx监护仪发布之后马上就要开发统一软件平台的新一代中央站,到那个时候再解决也不迟。
产品换代嘛,总得经历一个阵痛的过渡期。于是解决问题的重担落到新一代中央站的身上。这个问题已经被反复提出来讨论,有点炒剩饭的意思,已不如当年那般有震撼力。由于业务的优化,组织架构的调整,人员的重组,Tx监护仪的维护已由西研的团队接管,总部都投入到新一代监护系统Nx系列的研发中。在Nx监护仪上线发布后,原班人马"兼收并蓄"了中央站团队,因此总部都Focus在Nx系列上。当年参与讨论Tx监护仪和中央站待机屏蔽报警问题的人可能都已经不太参与实际代码的编程,其工作更多由团队成员完成,他们只是把控关键的技术风险问题。待机问题已成剩饭,有些细节大家早已模糊,而新成员根本就不清楚这里面的道道(大坑)。
Nx系列复用同一软件平台,对于待机下屏蔽除电池电量低外所有报警功能的测试,经过等同性分析被认为不需要了。因为在开发Nx监护仪时已对待机下所有报警的屏蔽做过细致全面的测试,中央站和监护仪复用前端架构及代码,开发人员分析可以完全等价。
Nx中央站研发时开始试水和西研团队的协同开发。响应BU的大号召,逐步将软件功能实现转移过去,总部只负责关键技术方案设计,技术风险把控以及产品质量控制。在这样的大背景下,Nx中央站的开发时,考虑到西研团队年轻经验不足,因此划分"业务简单"的前端部分功能到西研团队。
西研测试团队收到总部指示,潜意识认为Nx中央站的待机功能和监护仪等同。测试人员重点关注Nx中央站和上一代和前x代监护仪的交互。于是发现Tx监护仪和Nx中央站在待机时,中央站仍能触发报警的问题。西研团队懵圈了,不是和Nx监护仪一样报警列表都被监护仪正确处理过的吗,怎么连接Tx监护仪就出问题呢。于是报告给总部,当年掉坑里的中央站开发人员兴奋的叫起来:"妈蛋,老子当年就掉这坑了。Tx监护仪在待机下报警没屏蔽干净,为了给它擦屁股,不得已在上一代中央站上打补丁屏蔽报警。
这个问题已不能引起轰动,况且以后主流趋势是与新一代Nx监护仪互连。这次没有展开太多的讨论即达成结论,Nx中央站和Tx监护仪互连,只是为了向前兼容而已,他们迟早要停产的。因为互连Nx监护仪不会出问题,于是传递给西研团队解决方案是简单处理,避免影响扩散,增加测试人员的工作量。西研团队充分领会总部的精神,和老中央站一样打个补丁继续给Tx监护仪擦屁股。
这屁股擦的"万劫不复",新一代Nx中央站肩负解决连接Nx监护仪待机下不能报出电池电量低报警的问题。原本软件复用自然而然就没有问题。可又再次挖出当年和Tx监护仪的旧债,作为仅负责开发新一代中央站部分前端功能的人来说,他能做的只能按照过去一样的补丁方式处理。但要是考虑全面一点的话,至少判断下连接Tx监护仪才走补丁的逻辑,对于Nx监护仪则不处理。但时代变了,监护系统的架构变得更加精细,架构约束前端不允许针对连接的特定监护仪机型做特殊处理。也即是说,中央站前端获取不到当前连接的监护仪机型。况且西研中央站前端开发人员压根就不知道Nx监护仪那么"变态",还有报警在待机下需要触发出来。另一方面前端开发人员甚至还可能"自鸣得意"考虑周详,既然无法获知机型,那就等同了,而且杜绝以后Nx监护仪万一出现待机下报警屏蔽不干净的问题时,中央站可以"独善其身"。可不曾想中央站需要连接"无数种"的床旁监护仪。这种单方面的以偏概全的补丁解决故障表面现象的背后,其影响是巨大的。
最终新一代Nx中央站连接Nx监护仪时,同样出现本文开篇提到的问题,待机下中央站不触发电池电量低的报警。层几何时这个问题在上一代产品,上一代和新一代过渡期都出现过。似乎这个问题是幽灵般让我们陷入怪圈。而且每次由不同方向的人提出来,这次的测试人员是个不太熟悉"业务"(不知道水有多深)的新员工,甚至他一开始还认为是Nx监护仪的问题,为啥待机下电池电量低的报警未屏蔽。
这次测试负责人找到中央站总工反馈问题,总工高瞻远瞩从临床风险分析,既然床旁监护仪报出来电池电量低的报警,那风险就可接受,而且一旦床旁监护仪退出待机,中央站就可以报出来,认为问题不大暂不更改。
总工英明啊,要是决定改,我们可以大胆推测后续更改之情形。因为反复出问题,这次更改很有可能会收回到总部处理,而且职级高的资深人员投入其中,甚至负责Nx架构的总工都要参与指导并审核。资深人员是Nx监护仪开发中成长起来的骨干,很快就发现问题所在,中央站不需要处理报警的屏蔽,Nx监护仪后端已处理正确,这个补丁直接删掉,故障回归。
然后故障回归验证时,Nx监护仪连接中央站待机下电池电量低的报警可以正确触发,但是连接Tx监护仪时本该在待机下屏蔽的其他报警却在中央站又报出来了。故障重打开被打回来,开发人员马上意识到问题原因。嘴上发着牢骚:"shit,Tx监护仪做得怎么那么烂,待机下报警都不屏蔽干净。"看来不能对所有机型都去掉这个补丁,至少对Tx监护仪不可以。鉴于更改影响,只能再次打补丁在中央站前端判断机型区分处理,Tx监护仪待机下清除所有报警,Nx监护仪不做处理。但架构不允许获取到机型信息,不过这似乎难不倒机智的开发人员,Nx总有些特定的信息,收到这个信息就认为是Nx机型。这种"曲线救国"的补丁其实在挖更大的坑,而且还在坑上铺了稻草等伪装物。将来一旦Tx监护仪某个版本增加发送补丁用来区别机型的信息,中央站会误认为Tx机型是Nx机型,对待机下的报警不做屏蔽处理,导致Tx监护仪本应该在待机下屏蔽的报警在中央站再次显示出来。而且中央站测试人员几乎从不关注床旁监护仪的版本,发现问题的时候提单为必发故障。等到开发人员查问题确认现场时,就傻眼了。明明当时必发的问题,怎么重现不了。觉得中央站是不是这段时间又改了啥,回退中央站到提故障的版本,仍复现不出来。开始怀疑人生,其实只是因为Tx监护仪的版本不同而已。老版本不发送用于机型判断的信息,不会掉坑里,新版本会发送,误判断为Nx机型而不做报警屏蔽处理。
当然这种"巧妙"的通过区分机型来解决问题的方式总工断然不会赞成的。这时开发人员跳脚了,这不行那不行的,你说怎么搞嘛?这时总工终于亲自深入代码,一眼就看穿猫腻。接着爆了粗口:"卧槽,怎么TMD这么搞。前端处理个毛业务啊,待机下清除报警的逻辑必须放到后端去。"病根终于找到了,确诊之后就对症下药着手启动更改。
病已到扁鹊望桓候而还走的地步,要根治得做手术才行。此前待机下屏蔽报警的逻辑放在前端,针对连接到中央站的所有机型都起作用,目前只发现连接Nx监护仪待出现待机下不报电池电量低报警的问题。可一旦移到后端来屏蔽待机下的报警,首先得考虑针对哪些机型进行屏蔽处理。要知道中央站除了连接Nx,Tx监护仪外,还有Vx,PM,TM等监护仪。如果仅针对Tx监护仪进行屏蔽处理,不排除其他非Nx系列监护仪存在待机下误报警的问题。如果针对除Nx系列外所有监护仪进行屏蔽处理,需要测试确认这些监护仪都不存在类似Nx监护仪电池电量低的特殊报警,否则同样面临目前Nx监护仪遇到的问题。
改与不改似乎都是两难的选择。主管中央站业务的总工在看到此文章前,即已英明决策不更改。实属万幸,否则一阵折腾后讲再次回到原点。我们只能期待将来这个问题不会被没有眼力的新人再次提出来。也许这是程序员的集体困境,问题反反复复出现却无法彻底解决。也许这也是程序员的出路,否则软件里面故障改一个少一个,没有故障程序员都得失业了。
本文是为程序员写的,我估计很多人看不到最后这段话,这是情理之中的事情。如果能坚持读到最后,肯定是有做好程序员的愿望和态度。如果还能从废话连篇的描述中梳理出整个问题演化的过程来,说明具有做好程序员所具备的逻辑思维的潜力。如果不但能梳理出事情的脉络,还能提出质疑来,要么是成为"大师"的潜质前奏,要么就是当年挖坑或者掉坑里的人。
要杀死一个程序员,变化三次需求足矣。作为程序员应该反思是需求真的变了吗?还是在沟通时选择性听取需求?还是在按照自己的理解意淫需求?还是在构思设计方案时"创造"需求?
有点乱,我们来梳理下整个更改过程的脉络。问题发轫于Tx监护仪在待机下报警屏蔽不彻底,中央站开发人员的"息事宁人"未推动Tx监护仪更改解决而在中央站打补丁规避,接着面对待机下要触发电池电量低报警的需求变更时的妥协,成为压死骆驼的最后一根稻草。至此一发不可收拾,该问题在Tx监护仪,Nx监护仪,新老中央站之间如幽灵般反复出现,愈演愈烈。最终无法收场不得不"打脱牙和血吞"。
待机下屏蔽除电池电量低外的所有报警的需求看似简单,以至于不受重视。这项需求的关键点在"屏蔽"二字。开发人员和需求人员所理解的"屏蔽"其实有所不同。开发人员理解也许只是界面不显示,而需求人员却认为屏蔽不仅仅界面不显示,而且不存储,还不能发送到中央站。开发过程中打着简化不过度设计的旗号,我们丢掉本质,仅界面不显示。如果能理解"屏蔽"的本质,中央站不至于仅在界面显示上屏蔽待机下的报警。
关于监护系统中监护仪和中央站各自承担的职责,以及前端与后端的职责,架构约束可能描述的言简意赅,很难让开发人员达成一致理解而遵守。在第一次发现Tx监护仪待机下报警屏蔽不干净,就应该推动监护仪端更改解决。即使出于进度,更改影响的考虑,中央站不应该在前端,而是要在后端的设备层打补丁。我们因为种种不遵守架构定义的职责划分原则,一步步的退让妥协导致最终无路可退。不谋全局者不足以谋一域。要彻底解决问题,却仍要从全局出发,回归初心。要么Tx监护仪解决待机报警不干净的问题,而中央站去掉补丁,要么中央站把补丁从前端移到后端针对存在问题的监护仪机型屏蔽报警。
本文言语戏谑,由真人真事改编而来。作为程序员的你们也许或多或少会看到自己的身影,如有雷同,纯属巧合,请勿对号入座。