论文翻译,用于个人学习和记录,英文和专业水平不足,很多地方翻译不出来或者翻译错了,如果有人看到了,希望不吝赐教
源文件是从该网址下载的 https://dl.acm.org
或者
链接:https://pan.baidu.com/s/1j2NjTulfHLVu5dvdWKYomA?pwd=p4ka
提取码:p4ka
如果要看我的翻译的话,建议和英文原版一起看,避免无法理解我的拙劣翻译
原文的引用标注了但是没有给出,建议下载英文原版查看
有些语句和单词能力不足,或无法理解其在软件工程上存在的特殊意义可能会不翻译或者翻译错误,还是建议和英文原版一起看!(最好就直接啃英文不用看我这个了)
DAVID LORGE PARNAS,PAUL C. CLEMENTS, And DAVID M. WELSS
摘要:这篇论文旨在讨论软件的组织结构,这个通常来说会因为细枝末节而变得复杂晦涩的部分,然而这正是需要纠正完善的。我们展示了一些诸如信息隐藏和抽象的软件设计技术可以通过结构化的层次结构文档补充,我们称该文档为模块向导。这个向导是为了同时帮助设计者和维护者能够方便简单的区分他们必须了解和他们无需了解的模块。这篇论文包含了一个对软件模块向导,我们以此说明我们的建议
索引词——抽象接口,信息隐藏,软件的模块结构,软件工程
五年多前,在Naval Research Laboratory 的一些人对我们提出的一些观点感到担忧,我们的观点认为现今在主要会议上(理论上?)所提倡的软件工程准则与在许多企业和政府实验室里的软件工程实践存在越来越大的割裂。会议和许多期刊充斥的着许多看起来很棒的想法(ideas),却仅仅是通过一些简单到不切实际的碎片(易碎品?)项目或者是没有在大量细节上完整解决的复杂问题。当我们检查实际软件项目和他们的文档时,几乎没有地方使用了上面那些想法,并且似乎没有成功的产品是在这些会议和期刊中所吹捧的设计原则下诞生的。这些想法只是纸上谈兵。
我们可以猜想几个导致这个割裂的原因:
这些想法,如许多老式(old-style)程序员宣称的那样,对真正的困难而言是不切实际的
负责人的管理者是不愿意在未经实践证明的准则上打赌的,从而产生启动问题(thus creating a startup problem)
论文中使用的案例与实际从业者碰到的问题相去甚远,以致无法用作参考
这些想法也许需要细化或者拓展之后才可以作为复杂且存在具体领域资源限制的项目的指导原则
从业者,如某些学者声称的那样,智力上无法胜任给予他们的工作。
以我们对这些想法和从业者的理解,不认为原因1和5是正确的;我们打算从原因2-4上下点功夫。
我们打算用一个无可辩驳地贴合实际的问题,并采用“学术性”的ideas,因此,如果我们成功了
这对负责人的管理者是这些ideas的可行性的一种证明
这可以成为类似问题的一个参考模型
我们可以细化或补充这些ideas直到它们可以在在那些文章中更复杂的系统中起作用。
我们选择对一个已存在的系统进行复刻,因为这样我们就能对这两种分别采用传统技术和新学术准则的系统进行比较了。选择的项目是为A-7E飞机设计的飞机操作程序(Operational Flight Program,OFP)。当前的程序(传统)采用了许多肮脏的伎俩(diarty tricks),只能勉强塞入内存(barely fits in its memory),并且勉强完成实时性的需求(barelly meets its real-time constraints)。因此,我们认为这个程序,即使体量上小于其他很多程序,但仍是对那些ideas的一个好的测试。因为当前的OFP被认为是同类(ilk)程序中最棒之一,我们认为最后的成功是在这样的项目上比在一个原本就烂的项目上更具有说服力(we considered the task sufficiently challenging that skeptics would not attribute our success to the poor quality of the program that we are trying to match)
尽管这个项目距离完成还有很长一段,但我们已经在三个目标上有了一些小成果。我们为软件书写一个完整且精确的要求规范的能力已经激励管理者尝试相同的方式,并且我们的文档 [9] 已经成为一些项目的参考。我们还发现了一些应当在项目开始前使用的有效的细化。比如说,抽象接口的概念,我们在 [1] 中讨论过的,现在已经在 [2] 和 [3] 中被细化和说明了。
这篇论文展示了我们打算使用的对准则的另一种细化。其中最基础的一个idea就是使用信息隐藏的准则 [6] 来将项目分解成工作任务或模块。这个idea是一个绝佳的例子来展示学术软件工程和实践中的割裂。尽管这个idea已经被某些学认为是自证的(self-evident)了,但我们认为找到任何体量较大(sizable)的产品中从一而终地使用了。尽管一些作者(author)认为这个idea已经“应该为人所熟悉”(“old hat”)了,我们仍没法说服那些人用和他们以前用的完全不同的方式来构造软件(we could not persuade those charged with building real software to do something so radically different from what they had been doing)
当我们尝试着使用这些idea时,我们发现虽然这相当适用(quite applicable),还是需要增加一些额外的且必要的ideas让其在含有多个(a dozen or so)模块的系统中起作用。这篇论文会讨论我们遇到的困难和额外增加的ideas。
软件系统的一个结构化描述展示了程序是如何被划分的,以及这些部分之间的关系。A-7E 的开发者必须留心三个结构:a)模块结构,b)使用结构,c)process 结构。本节将对比这三种结构。
a)一个模块是一个开发者或一个开发团队的工作任务。每个模块都包含一群紧密关联的程序。模块结构是将程序划分为模块的分解以及团队应对模块都要能够了解其他模块而负责的假设(and the assumptions that the team responsible for each module is allowed to make about the other modules)
b) 使用结构中,个体是程序,比如说,不是模块而是模块的一部分;关系是“要求什么东西的存在”。使用结构决定了软件的可执行子结构(excuteable subsets)。
c)process结构是对系统运行时的活动分解为可称之为processes的个体。Processes不是程序;process与模块之间也没有简单的关系。一些模块的实现可能包含了一个或多个processes,并且任何process都可能调用多个模块中的程序。
本文的剩余部分将讨论模块结构。
我们的模块结构基于在 [6] 中说明的分解准则——信息隐藏。在此准则中,可能独立改动的系统细节应当成为单独模块的秘密;仅有的出现在模块之间的接口的假设,应当是被认为不太可能改动的那些。每个数据结构应仅在一个模块内使用;这个数据结构也许会被模块内的若干个程序访问,但是绝不能被模块外的程序访问。任何试图访问一个模块内存储的数据结构的请求应当通过调用该模块的访问程序来访问。
应用这个准则并不总是很简单。这是一个试图最小化软件预期成本的尝试,并且要求设计者预估改动的可能性。这种预估基于过往的经验,可能还需要应用程序领域的知识,以及对硬件和软件技术的理解。因为一个设计者大概不会有这所有相关的经验,我们已经开发了一个正式的审查程序来从那些有着相关经验的人身上获取益处。这些程序在 [2] 中描述了。
将模块分解为模块的主要目的通过允许模块被独立地设计和修改,以此达到减少整体的软件成本。某块分解的一些具体的目标如下。
a)每个模块的结构都应该足够简单,让人能够完全地理解。
b)修改某个模块的实现应该基于在不了解其他模块的实现和不影响别的模块的行为的前提下。
c)在设计上做出的改动应该合理地基于所需改动发生的可能性。做出不需要改变任何模块接口的改动是最有可能的;包含了接口修改的改动应是更低可能性的,并且只能是那些小且并没被广泛依赖的模块。只有非常不可能的改动能要求被广泛依赖的模块的接口修改。
d)a set of在独立的模块中做出各自独立的改动是可能的,比如说期望接口修改,开发者改变了没必要沟通的独立模块。如果模块的接口没有调整,那么无论使用旧还是新版本的模块,整个系统都能跑起来。
基于上述的多个目标,我们的软件是由多个小模块组成的。在先前使用信息隐藏的尝试中,我们已经见证了一个有着5-20个模块的系统。现在我们知道了,我们会有数目过百的模块。如果说是25个或者更少的模块数量的话,想要知道某个改动可能影响到的模块就会很苦难。如果是上百个模块就另说了。在25个或更少模块时,仔细的检查可能可以确保没有东西被忽视。但是在上百个模块的时候几乎是不可能的。我们意识到对信息隐藏的使用可能适得其反了。因为很多维护者会忽视很多模块的内部结构,维护者会在很多模块中搜索直到找到他们需要的那个。在发现我们遗漏了一些主要模块之前,我们还担心工作了一段时间。(We also feared working for some time before discovering we had left out some major modules.)
我得出了必须在应用信息隐藏准则时增加额外规则的结论,并且如果我们打算减少维护复杂软件系统的成本,我们就需要一个特殊的文档。我们已经找到了一种处理small lists of模块的方法以便我们能证明每个list都是完整的。我们需要准备一个软件工程模块向导,这个东西能帮助维护者找到被某个改动影响的模块,或者哪些模块造成了某个困难。
基于此考量,模块会被组织成为一个树结构层次;树中的每个非叶子节点(nonterminal node)代表着一个由其子孙组成的一个模块。层次结构已被记录为一个模块向导[9]。层次结构和向导旨在达到以下的额外目标。
e)一个软件工程师应该能在不了解模块内部实现的情况下理解模块的职责。
f)一个有着明确关注点的读者应该能简单的辨别哪些关联的模块而不需要去学习哪些不关联的模块。这意思是读者能在不看他们的组成的前提下区分相关的模块和不相关的模块。
g)非叶子节点模块的在图标中的分支应该足够少,这样设计者才有底气能说子模块没有重叠的职责,并且还确实覆盖了所有的模块想要达成的职责。在最开始的设计中,这是最有价值的,但在辨别受改动影响的模块时也能有所裨益
我们发现在实际的系统中并不总是能够确定什么信息是该属于某个单独的模块。比如说关于硬件的信息是可以替换的这个是肯定的,但是关于硬件的诊断信息必须与用于将信息展示给用户和硬件维护者的模块沟通。在硬件改动时,任何使用这个信息的程序都有可能变动。为了减少软件改动的成本,对提供这类信息的模块的使用应该做出限制。受限制的接口在向导中被标记了“(R)”。通常某些较小模块的存在本身就是较大模块的秘密。在一些情况中,我们已经在这个文档中提及了这样的模块来明确某些函数执行的位置。将这些模块称为隐藏的模块,并且在文档中标记为“(H)”。
模块向导展示了职责是如何分配到主要模块的。这样的向导是为了让读者能找到实现了系统特定方面的模块。这个文档声明了将特定职责分配个某个模块的原则,还把模块编排好让读者找到和他目标相关的信息而不需要查找不关联的文档。向导定义了范围(scope),和独立设计文档的内容。
三种描述一个基于信息隐藏的模块结构的方法如下:
根据独立模块在系统整体运作中扮演的角色。
根据与每个模块相关的秘密。
根据每个模块提供的设施(facilities)。
模块向导通过characterizing每个模块的秘密来描述模块结构。Where useful, a brief description of role of the module is included. 模块facilities的细节描述被放到了其他的文档,称之为“模块定义”。比如, [2] 。模块向导会告诉你哪些模块需要改动。模块定义会告诉你如何使用模块和模块必须做什么。
对于某些模块,我们发现区分主要秘密和次要秘密是很有用的。主要秘密指的是针对于软件设计者的隐藏信息,而次要秘密则是指设计者在试图隐藏主要秘密时采用的实现决定。
在模块向导,我们尝试尽可能精准的描述分解的规则,但未来技术上的改动使得某些边界模糊了。在这种情况下,我们会注意模糊的区域,并讨论用于解决歧义的额外信息。
为了展现我们的技术如何运作,我们给出A-7OFP模块向导中一个相当庞大的提炼。我们会在提炼后讨论这个东西如何帮助构建和维护。
我们展示的设计是由Naval Research Laboratory(NRL,海军研究实验室)生产的A-7E飞行软件中模块结构。A-7E飞行软件是硬实时(hard real-time)程序,用于处理飞行数据和控制飞行员的屏显。软件通过一个惯性导航系统计算飞机的位置,并且必须高度精确。当前的操作程序is best understood as 一个大模块。在实现某个特定的改动要求时很难区分必须要修改的程序的部分。我们的软件结构就是为了满足上面提高的需求而设计的,但必须仍满足所有的准确和实时性的限制。
以下是对NRL版本的软件的模块向导的提炼 [7] 。向导的完整备份或者任何其他NRL的报道能通过向写信给NRL获取。
软件系统包括以下三个模块。
硬件隐藏模块包含了这样的程序——当硬件的任何部分被相同能力但不同接口的新unit取代时,需要变动的。这个模块实现了一个虚拟硬件给系统的其他部分使用。该模块的主要秘密是在文档 [9] 中篇章1和2里描述的硬件—软件接口。该模块的秘密是数据结构和用来实现虚拟硬件的算法。
行为隐藏模块包含了这样的程序——当像需求文档中描述的需求行为的部分 [9,ch.3,4] 发生改动时,需要变动的。该模块的主要秘密是这些部分的内容。这些程序决定被送往硬件隐藏模块提供虚拟输出设备的值。
软件决策模块隐藏了软件设计决策,包括基于数学理论,物理事实和编程考量如算法效率以及精确性。该模块的秘密不在requirements文档中。这个模块和其他模块的区别包括两点,秘密和接口是由软件设计者决定的。这些模块的改动更可能源自改善表现的尝试,而非外部强加的改动。
上面的定义存在的模糊性源自以下几点
a)需求定义和软件设计之间的界限已在写下需求文档时被决策部分确定了;比如说武器弹道模型也许被系统分析家挑选好并在需求文档中指定了,又或者通过声明准确度需求但不指定算法而被留给软件设计师审慎决定。
b)硬件特征和软件设计之间的界限难以区分。硬件可能构建来取代当前软件完成的服务;因此,那些模块可以既可以视作隐藏硬件特征的模块,也可以视作隐藏软件设计决策的模块。
c)硬件上、系统行为上的改动或用户可能会使得软件设计决策不那么合适。
d)所有的软件模块都包含了软件设计决策;任何模块都有可能因为效率和精确度的考虑而做出改动。
这样的模糊性对我们的目标而言是无法接受的。我们可以通过一个精确的需求文档如 [9] 来排除模糊性。这个文档指出了行为、硬件,软件决策之间的界限。
a)当需求文档指定了一个算法,我们不认为算法的设计属于软件设计决策。如果需求文档只声明了算法必须满足的需求,那么我们认为实现了这个算法的程序是软件设计决策模块的一部分。
b)软件和硬件之间的接口在软件需求文档中指定。硬件特征和软件设计之间的界限必须基于对未来改动可能性的预测。如果硬件未来实现某个特定设备是相当有可能的,那么实现该设备的软件模块就该划分到硬件隐藏模块。我们一贯采取保守立场;该设计基于这样的假设,即剧烈改动的可能性低于改革变动。如果在硬件隐藏模块方面有些改动。如果有激进的改动试图取代先前由软件支撑的服务,一些软件决策模块会被移除或者缩减大小。
c)一个模块只有在需求文档发生改动的时候仍然有用(尽管可能不再高效)时,才能算软件决策模块
d)一个模块内的秘密不包括软件需求文档中写出的信息时,才能算在软件决策范畴中。
硬件隐藏模块包含两个模块。
拓展计算器模块隐藏了航空电子计算器的硬件—软件接口,这些是我们认为计算器修改或替换时可能会改动的。
航空电子计算器之间的硬件—软件接口、硬件中直接实现的能力都是各不相同的。比如说一些航空电子计算器内置了实数的浮点近似值,然而其他的通过 a programmed sequence of fixed-point operations 来实现对实数的近似操作。一些航空电子系统是单处理器;其他的系统是多处理器。拓展计算器提供能在绝大多数航空电子计算器中高效使用的指令集。这个指令集包括了对应用无关的数据类型的操作、序列控制操作、和一般I/O操作。
拓展计算器的主要秘密是:处理器的个数,计算器的指令集,和计算器处理并发操作的上限。
拓展计算器模块的结构见C.1.1。
设备接口隐藏模块隐藏了那些被认为可能改动的外围设备的特征。每个设备都有可能被更好的上位替代物所取代。更换的设备在他们的硬件—软件接口上有着较大的不同。比如说,所有的攻击角度传感器会计算飞机上的基准线和周围气团的速度之间的角度,但是他们的输入格式,timing,和数据中的噪点是不同的。
设备接口模块提供虚拟设备给系统的其余部分。虚拟设备并不一定要和物理设备对应,因为所有提供能力的硬件并不一定在同一个物理unit中。并且,单个物理unit中的一些能力也可能独立于其他能力而改动;隐藏那些会在不同模块中独立改动的特性时很有用的。
设备接口模块的主要秘密是:卸载需求文档中的当前设备的特性,和不太可能被上位取代的特性(not likely to be shared by replacement devices)
设备接口模块的结构见C.1.2。
硬件部分在设计CPU的人眼中是外部设备,但在某些其他文档中却是处理器的一部分。我们对计算器和设备的区分基于当前的硬件以及遵循需求文档内容。应用于多个设备的信息被认为是拓展计算器的秘密;而仅关联单个设备的信息则是设备接口模块的秘密。比如有说一个用于在多个设备中沟通的模拟—数字转换器;这个转换器被拓展计算器隐藏,尽管它也可以视作一个外部设备。又比如另一个例子,现有为了测试I/O通道的特定输出;他们不和某个单独的设备关联。这些输出是拓展计算器的责任。
如果所有的硬件在同时被替换,可能会在计算器和设备之间出现职责的重大转移。在如A-7E的系统中,这样的改动并不常见;单独设备的替换和单独替换计算器的情况更加常见。我们的设计是基于这种替换模式将继续保持的预期。
行为隐藏模块包含两个模块:一个共享服务(SS)模块和由前者支撑的函数驱动(FD)模块。
函数驱动模块包含一套独立模块,称之为函数驱动器;每个函数驱动器是一套紧密关联的输出的唯一控制器。当将输出的值在一起描述比各自描述更加简单的时候,认为这些输出就是关联紧密的。比如说,如果某个输出是一个角度的sin值,另一个是该角度的cosine值,那么联合描述将会比两个分别描述smaller。需要注意的是,函数驱动模块和硬件隐藏模块创建的虚拟设备打交道,而不是物理输出。函数驱动模块的主要秘密是:决定这些输出的规则。
函数驱动模块结构见C.2.1
因为所有的函数驱动器在同一架飞机上控制系统,行为的某些方面在多个函数控制器之间可能会重合。我们希望如果行为的某个方面发生了改动,那么这会影响所有共享了该方面的函数。因此,我们确定了一套模块,每个都隐藏了一个应用了多个输出的行为的方面。
共享服务模块结构见C.2.2
因为文档的使用者大概是不知道函数行为的哪一方面被共享了,函数驱动模块的文档会包含它所使用的共享服务模块的引用。一个维护者应当总是从向适当的函数驱动模块查询开始。他会在合适的时候被导向共享服务模块。
软件决策模块被分为了
应用数据类型模块,隐藏了某些变量的实现。
物理模型模块,隐藏了用于模拟物理现象的算法。
数据banker模块,隐藏了数据更新政策。
系统生成模块,隐藏了那些决策——被推迟到系统生成时间的。
软件公用工具模块,隐藏了被多个其他模块中使用的算法。
应用数据类型模块,较之拓展计算器模块提供的数据类型,做了补充、提供更多数据类型,对航空电子应用有用且不需要依赖计算器实现。这些数据类型是基于拓展计算器提供的数据类型实现的;这些类型的变量在使用时就像这些类型是在拓展计算器内置中的一样。
应用数据类型模块的秘密是变量中使用的数据表示和在这些变量上实现操作的程序。这些变量可以不考虑单位地使用。必要时,模块提供转换运算符,以指定单位传递或接收实际值。
运行时效率的考虑有时要求应用数据类型的实现基于另一个模块的秘密。在这种情况下,这个数据类型会在应用数据类型模块的文档中指出,但是实现会在文档中描述,且会包含适当的引用。
应用数据类型模块结构见C.3.1
软件要求若干的预料,数量无法直接测量但是可以通过物理世界中可观察的模型来计算。物理模型模块的主要秘密是是物理模型;次要秘密是计算机对这些模型的实现。
物理模型模块的结构见C.3.2
多数数据由一个模块生产,然后被另一个模块“消耗”。通常,消耗者应该收到尽可能最新的值。数据banker模块更像一个“中间人”,他决定这些数据的新值何时该计算。数据banker从生产者处获取值;消耗者程序通过数据banker的访问程序来获取数据。The producer and consumers of a particular datum can be writter 而不知道数据banker是否存储值或一个存储好的值是否已更新。多数情况下,当更新政策改动时,生产者和消耗者都不需要修改。
如果消耗者要求生产者计算出的序列中的某个特定成员,或者他们要求关联于某个特定时间——比如某个事件发生的时候的值,就不该使用数据banker。
一些能在数据banker中使用的更新政策如下表,两列分别表示数据banker是否存储某项的复制和何时计算新值。
| Name | Storage | When new value produced |
|---|---|---|
| on demand(需要时) | No | 当消耗者需求该值时 |
| periodic(周期性的) | Yes | 周期性的,消耗者会得到最近存储的值 |
| event driven(事件驱动的) | Yes | 当数据banker被通知时,通过某个事件的发生,更新值。消耗者会得到最近存储的值 |
| conditional(有条件的) | Yes | 当消耗者需求值,且特定条件满足时。否则,发送先前存储的值 |
在这些或者其他更多的更新政策中的选择,应当基于消耗者对准确性的需求,消耗者需求值的频率,消耗者所能等待的最长事件,值变化的频率,以及生产一个新值的成本。因为这些决定并不基于消耗者和生产者的编码细节,当生产者和消耗者改动时通常没必要重写数据banker模块。
系统生成模块的主要秘密是:被推迟直到系统生成时间的决策。包括了系统生成参数的值和在可替换的模块之间的选择。系统生成模块的次要秘密是用来生成代码的机器可执行格式的方法和被推迟的决策的representation。该模块中的多数程序不运行在车载电脑上;他们运行于一个更强大的电脑上来生成车载系统的代码。一些程序是我们系统提供的工具;另一些则是专门为这个项目开发的
这个模块的主要秘密是那些实现了相同软件函数的算法,比如说资源监视器模块,和数学routines,比如平方根和对数运算。
注意:处于本文的目的,仅包含描述特别具有说明性的第三级模块。省略号表示遗漏
数据类型模块实现了变量和实数的操作,时间段(time periods),和bit strings。数据表示和内置于计算器硬件的数据操作指令是该模块的主要秘密——特别是,数字对象在硬件数据类型方面的表示;bitstring的表示;如何访问bitstring中的bit;以及如何表示硬件计时器的时间的。该模块的次要秘密是范围和需求是如何决定表示的;执行数字运算的程序;执行bitstring操作的程序;以及如何通过一个数组名字和下标获取数组元素的内存位置的。
...
计算器状态模块持续追踪拓展计算器当前的状态,其可能是操作中,关闭,或失败,并向用户程序发送相关状态变化的信号。主要秘密是:硬件检测和造成状态变化的方式。在EC已被初始化后,这个模块将触发初始化系统其余部分的事件。
...
诊断模块提供诊断程序来测试硬件中断,I/O硬件,以及memory。该模块的使用被限制了,因为该模块返回的信息会揭示拓展计算器的秘密,比如说,如果航空电子计算器被其他计算器替代了,那么使用这些信息的程序或许就需要修改。
虚拟内存模块提供了一个均匀的可寻址虚拟内存给数据, I/O,和序列子模块,允许他们为了数据或子程序使用虚拟地址。虚拟内存模块的主要秘密是硬件存储数据的方式和实际内存中的指令;不同内存区域寻址方式的差异是隐藏的。该模块的次要秘密是分配实际内存到虚拟地址的政策以及将虚拟地址引用转换为实际指令序列的程序。
...
下表描述了设备接口子模块(DIM‘s)和他们的秘密。“如何阅读...“的短语旨在较自由的被解释,比如说,它包含了设备相关的更正,筛选,以及其他任何对于由设备输入决定一个物理值而言是必要的动作。所有的DIM‘s 隐藏了对他们控制的设备的测试程序。
| Section | 虚拟设备 | 秘密:该模块是如何... |
|---|---|---|
| C.1.2.1 | 空气数据计算器 | 读取气压高度,真实空速,和马赫数 |
| C.1.2.2 | 攻击角度的传感器 | 读取攻击的角度 |
| ... | ||
| C.1.2.20 | 武器释放系统 | 辨别飞机请求的武器释放动作;使得武器预备和释放 |
...
下表描述了函数驱动子模块和他们的秘密
| Section | 函数驱动器 | 秘密 |
|---|---|---|
| ... | ||
| C.2.1.7 | HUD函数 | 可移动的HUD标志应该放在哪里。一个HUD标识应当开启,关闭或闪烁。固定显示处应该放什么信息。 |
| C.2.1.8 | 惯性测量装置函数 | 决定IMS速度测量中的标尺的规则。什么时候初始化速度测量。IMS校准时可旋转的角度 |
| C.2.1.9 | 面板函数 | 什么信息要放置在面板视窗。enter light什么时候应该打开。 |
| ... |
共享服务模块由以下模块组成
模式决定模块决定系统模式(正如在需求文档中定义的那样)。该模块在模式切换时,会发出信号,并且会激活当前模块的身份标识。模式决定模块的主要秘密是:需求文档中的模式切换表。
一个系统数值子模块会计算一系列的数值,其中一些可能被多个函数驱动器使用。该模块的秘密是:需求中决定数值如何被计算的规则。需求中的共享规则制定了这一类——1)多个可替换资源中的选择,2)对其他模块生产的数值的应用筛选,或者3)对其他地方计算的数值施加的限制。
该模块可能包含一个仅在一个模块驱动器中使用的数值,如果用于计算该数值的规则和用来计算其他共享数值的规则一样的话。
每个系统数值子模块还负责触发根据其计算的值定义的信号事件。
...
应用数据类型模块分为两个子模块。
系统数据类型模块实现了以下被广泛使用的变量类型:加速度,角度,角速率,字符文字,密度,马赫数,距离,压力和速度。这些模块也可能用来实现一些受范围限制或特定解释的类型(比如说,用角度用来表示维度)
STE模块实现了有限状态的的实例的变量。用户可以等待变量 到/从特定状态值的转换,促使转换并比较值是否相等。
物理模型模块包含以下模块
地球模型模块隐藏了地球的模型和它的大气层。这些模型包括本地重力的模型,地球的曲率,海平面压力,磁场变化,本地地形,以及地球的自转,科里奥利力,大气密度。
飞机运动模块隐藏了飞机的运动。他们用来计算飞机位置,速度和attitude from observable inputs。
空间关系模块包含了三维空间的模型。这些模型用于用于协调坐标变换以叫角度和距离计算
人为因素模型是基于飞行员反应时间的模型和感知模拟连续运动的模型。模型决定了显示器上标识的更新频率。
武器行为模块包含了用来预测武器释放后的行为的模型。
我们当下得出的任何结论都只是暂时的,因为他们还未被实际的运行所证明。尽管如此,我们已经使用模块向导许多年了,而且也确实很可靠。它在我们的开发流程中起到了很重要的作用;当开发者和设计者不确定某个模块的位置时,他们就会求助于模块向导。许多的讨论通过这种方式解决,而且这些讨论的最后结论都是相当少,且相当浅的改动。
我们的经验告诉我们信息隐藏的使用在复杂系统中是切实可行的,但仅在这种情况下——即设计是从,一个用来指导独立模块接口的模块向导,而开始设计的。当我们尝试不适用向导工作时,许多问题从破烂的构造中溜走,而且职责的分配最终也会要么落在两个模块,要么直接没有。有了模块向导,设计上的进一步的进展已经揭示了相当至少的疏漏。新加入团队的开发者可以迅速掌握项目的结构的脉络而不需要和老成员沟通很久。我们觉得这可以改善Brooks的格言,"Adding more men then lengthens, not shortens, the schedule" [8]。
我们指导我们用来说明的模块向导的描述算是戛然而止。向导中提到的多数模块都划分成了子模块,但这些子模块没有讨论到。我们发现使用为小模块使用独立的模块向导要比拓展某个向导更加的方便。这个模块向导是所有实现者都必须要阅读的文档;其他的则是给特定的人。这个向导长度小于30页,且 we can afford to let everyone read it。
在写这个和其他的模块向导时,我们发觉重要的是专注于描述模块的秘密而不是模块的角色。当我们忘记这一点的时候(通常是我们迫于deadline时),最终模块并不具备清晰的职责然后要调整这些模块。
模块向导,像我们的需求文档,提供了对我们称之为“通过文档设计”的方法的好处的清晰说明 [4] 。写文档是我们在设计上进步的方法。文档之后会在未来的设计上指导我们和其他人。
在另一个论文中 [10] ,我们已经讨论了这种方式可以增加我们产出的软件更加可复用的可能性。那个论文使用了相同的例子讨论了相当不同的点。
我正在使用i18n从头开始构建一个多语言网络应用程序,虽然我自己可以处理一大堆yml文件,但我说的语言(非常)有限,最终我想寻求外部帮助帮助。我想知道这里是否有人在使用UI插件/gem(与django上的django-rosetta不同)来处理多个翻译器,其中一些翻译器不愿意或无法处理存储库中的100多个文件,处理语言数据。谢谢&问候,安德拉斯(如果您已经在rubyonrails-talk上遇到了这个问题,我们深表歉意) 最佳答案 有一个rails3branchofthetolkgem在github上。您可以通过在Gemfi
很高兴看到google代码:google-api-ruby-client项目,因为这对我来说意味着Ruby人员可以使用GoogleAPI-s来完善代码。虽然我现在很困惑,因为给出的唯一示例使用Buzz,并且根据我的实验,Google翻译(v2)api的行为必须与google-api-ruby-client中的Buzz完全不同。.我对“Explorer”演示示例很感兴趣——但据我所知,它并不是一个探索器。它所做的只是调用一个Buzz服务,然后浏览它已经知道的关于Buzz服务的事情。对我来说,Explorer应该让您“发现”所公开的服务和方法/功能,而不一定已经知道它们。我很想听听使用这个
如果特定语言环境中缺少翻译,如何配置i18n以使用en语言环境翻译?当前已插入翻译缺失消息。我正在使用RoR3.1。 最佳答案 找到相似的question这里是答案:#application.rb#railswillfallbacktoconfig.i18n.default_localetranslationconfig.i18n.fallbacks=true#railswillfallbacktoen,nomatterwhatissetasconfig.i18n.default_localeconfig.i18n.fallback
在使用rails4和https://github.com/globalize/globalize的情况下,我应该如何为我的模型编写表单?用于翻译。我想以一种形式显示所有翻译,如下例所示。我在这里找到了解决方案https://github.com/rilla/batch_translations但我不知道如何实现它。这个“批量翻译”是一个gem还是什么?以及如何安装它。EditingpostEnglish(defaultlocale)SpanishtranslationFrenchtranslation 最佳答案 批处理翻译gem很旧
我有以下python函数来递归查找集合的所有分区:defpartitions(set_):ifnotset_:yield[]returnforiinxrange(2**len(set_)/2):parts=[set(),set()]foriteminset_:parts[i&1].add(item)i>>=1forbinpartitions(parts[1]):yield[parts[0]]+bforpinpartitions(["a","b","c","d"]):print(p)有人可以帮我把它翻译成ruby吗?这是我目前所拥有的:defpartitions(set)ifnots
所以我知道如果我在读取yaml文件时遇到“翻译缺失:”如何返回默认值。some=I18n.t("something.something_else",default:"value")但是如果我希望默认值为nil,我该如何以Ruby的方式做到这一点呢?我知道我可以正则表达式并匹配变量some中的“translationmissing:”,如果它匹配,我会将它分配给nil。但我想做的是拥有some=I18n.t("something.something_else",default:nil)但它只是返回了我缺少的翻译。有谁知道好的方法吗? 最佳答案
CSDN优秀解读:https://blog.csdn.net/jiaoyangwm/article/details/1266387752021https://arxiv.org/pdf/2103.14259.pdf关键解读在目标检测中标签分配的最新进展主要寻求为每个GT对象独立定义正/负训练样本。在本文中,我们创新性地从全局的角度重新审视标签分配,并提出将分配程序制定为一个最优传输(OT)问题——优化理论中一个被充分研究的课题。具体来说,我们将每个需求方(锚框)和供应商(GT标签)的单位传输成本定义为他们的分类和回归损失加权之和。在公式化后,找到最好的分配方案即为最小传播成本解决最优传输方案,
这是我的简单测试程序(使用ActionMailer3.0.8,Ruby1.9.2p180MacOSX):require'rubygems'require'action_mailer'ActionMailer::Base.delivery_method=:smtpActionMailer::Base.smtp_settings={:address=>"my_exchange_server",:port=>25,:domain=>'my_domain.org',:authentication=>:login,:user_name=>'my_user',:password=>'my_pass
有人知道如何在Rails中翻译模型关联吗?例如:我有一个Person模型,它可以有很多Phone。但是,一个人需要至少有一部电话。我无法翻译该验证。我能做的最好的是:validates_presence_of:phones,:message=>"Atleastonephoneisrequired."在我的YAML上,我替换了这一行以省略%{attribute}:format:!'%{message}'这样只显示我的消息,我避免显示未翻译的字段名称。这让我很头疼,因为有些gems根本不允许我传递:message=>"somethingdescribingtheerror",所以我想配置所
我创建了一个gem(TranslationsGem),我在多个项目(一个引擎和一个Rails应用程序)中使用它。这个gem设置了几个哈希值,这些哈希值被加载到I18n后端。#store_dynamic_translations方法设置了几个哈希,这些哈希被加载到I18n后端。它基本上是这样工作的:I18n.backend.store_translations(:en,{test:{property:'value'}})我的测试确认方法和翻译加载工作正常。但是我无法让它在主机引擎和Rails应用程序中工作。在我的测试环境中,我必须在我的test_helper中执行该方法,以确保正确加载翻