软件工程

针对在系统开发生命周期中工作的专业人士,学者和学生的问答

3
API对象定义包含第三方引用ID作为属性是否不好?
像这样: Campaign: type: object properties: id: type: string description: "A GUID identifier" referenceId: type: string description: "A consumers identifier they have used to map their own systems logic to this object." name: type: string description: "'Great Campaign 2017' as an example" 我担心referenceId。 系统域是一个平台,该平台通过数据导出和各种格式(xml,excel)的导入以多种方式与第三方集成。它已经足够成熟,可以允许第三方通过API与我们的系统集成,而该API的设计正是引发这一问题的原因。 我们有一个名为Campaign的对象,该对象的ID可用于识别和检索资源。我们API的消费者可能在自己的域内拥有自己的参考代码,以作为他们认为是广告系列的参考代码。 我们系统中还有其他带有第三方参考字段的对象,这是我们现有消费者所期望的。但是我担心这给我们增加了映射的负担,我们不知道这个referenceId是什么(数字,文本,json?),并且为新使用者增加了另一个令人困惑的属性。 在API的公共对象定义中允许第三方引用ID字段被视为不良做法或不良设计?

1
业务逻辑与服务层
我读了这个答案:https : //softwareengineering.stackexchange.com/a/234254/173318请更正我的理解。 业务规则是指现实世界中的业务步骤列表(无代码)。 业务逻辑是指将业务规则转换为代码的过程,并将这些代码束/种类称为“业务逻辑”。 服务层的用途是什么?如果我读了这个答案,听起来与业务逻辑没什么不同https://stackoverflow.com/a/4817935/4190539 服务层是业务逻辑和存储库相遇的地方吗?

1
软件/固件自动更新策略
我现在有一个中型项目,该项目已经接近“为客户演示提供草率咖啡因的原型”阶段的结尾,并过渡到“思考未来”阶段。该项目由具有软件和固件的基于Linux的设备以及中央管理Web服务器组成。目前存在10个原型,预计产量将低至1000左右。 我不熟悉自动更新技术,而且时间紧迫,所以我迅速制定了自己的软件部署/自动更新策略,坦率地说,它很糟糕。当前由以下各项组成: 带有生产发行分支的托管git repo(GitLab)(请注意,Web服务器源也位于此相同的repo中,还有其他一些东西)。 Web界面上的“部署更新”按钮,该按钮: 从生产发行分支将最新版本拉到本地回购区域,并将其复制到临时软件包准备登台区域。 在登台区域中运行清理脚本(存储在仓库中)以删除不相关的源文件(例如服务器源,固件源等)和.git文件。 将当前git哈希写入更新包中的文件(目的将在下面变得清楚)。 如果一切顺利,它将用gzip压缩并准备好使用,方法是使用同名文件覆盖以前的gzip压缩包,然后删除暂存区。 请注意,服务器上现在有两个当前设备软件的副本,它们有望同步:最新生产分支上的完整本地git repo和一个现成的gzip压缩包,现在假定该软件包代表了以下内容:相同的版本。 设备上的软件独立包含在名为的目录中/opt/example/current,该目录是该软件当前版本的符号链接。 引导时设备上的自动更新功能: 检查do_not_update文件是否存在,如果文件存在,则不采取进一步措施(有关开发设备,请参见下文)。 从上述文本文件中读取当前提交哈希。 使用该哈希作为查询参数向服务器发出HTTP请求。服务器将以304(哈希为当前版本)响应,或者将提供压缩后的更新程序包。 如果收到更新包,则/opt/example通过以下方式安装: 提取更新的软件信息,将其命名为stage。 从更新包运行安装后脚本,该脚本执行诸如对该更新进行必要的本地更改等操作。 将当前软件的根文件夹复制到previous(previous如果存在,则首先删除现有文件夹)。 将stage文件夹复制到latest(latest如果存在,则首先删除现有文件夹)。 确保current符号链接指向latest。 重新引导设备(固件更新(如果存在)在重新引导时应用)。 在新建设备上也存在初始部署的问题。这些设备当前基于SD卡(存在其自身的问题集,不在本文范围内),因此此过程包括: 存在一个SD映像,上面带有该软件的某些稳定早期版本。 从该映像创建SD卡。 首次启动时,会进行各种首次特定于设备的(基于序列号)初始化,然后自动更新程序将照常获取并安装该软件的最新生产版本。 另外,我需要开发设备的支持。对于开发设备: 完整的本地git repo会保留在设备上。 该current符号链接指向开发目录。 do_not_update存在一个本地文件,该文件可防止自动更新程序随生产更新一起删除开发代码。 现在,从理论上讲,部署过程应为: 一旦准备好部署代码,就将其推送到发布分支。 按下服务器上的“部署更新”按钮。 该更新现已生效,设备在下次检查时将自动更新。 但是,实践中存在大量问题: Web服务器代码与设备代码位于相同的存储库中,并且服务器具有我要执行的本地git存储库。最新的Web服务器代码与最新的设备代码不在同一分支上。目录结构有问题。当“部署更新”按钮从生产分支中提取最新版本时,会将其提取到服务器代码的子目录中。这意味着当我从头开始部署到服务器时,我必须通过将设备生产分支捕获到该子目录中来手动“播种”该子目录,因为如果我不进行部署,则可能是由于git user错误造成的。从父目录的Web服务器分支中提取设备代码。我认为这可以解决,方法是使暂存区不是服务器本地git repo的子目录。 Web服务器当前不永久维护设备软件的git哈希。在服务器启动时,它会git rev-parse HEAD在其本地设备软件仓库中执行,以检索当前哈希。由于某些原因,我无法全神贯注,这还会导致大量逻辑错误,在此不再赘述,足以说明有时重新启动服务器会使事情搞砸,特别是如果服务器是全新的并且没有生产分支仓库已被撤消。如果需要的话,我很乐意分享该逻辑的源代码,但是这篇文章越来越长。 如果清理脚本(服务器端)由于某种原因而失败,则服务器将拥有最新的存储库,但同步/丢失更新包不完整,因此git rev-parse HEAD将返回与实际不匹配的哈希服务于设备,并且必须在服务器命令行上手动更正此处的问题。即服务器不知道更新程序包是不正确的,它只是始终以纯属为前提。结合以上几点,使服务器在实践中极为脆弱。 最大的问题之一是:设备上当前没有运行单独的updater守护程序。由于等待wifi上网的复杂性和最后一刻的黑客行为,它是检查和更新设备的主要设备控制软件。这意味着,如果某种程度上未经测试的版本将其投入生产,并且控制软件无法启动,则实际上存在的所有设备都是砖砌的,因为它不再能够自我更新。这将是生产中的绝对噩梦。如果单个设备在不幸的时间断电,则同样的处理方法。 另一个主要问题是:不支持增量更新。举例来说,如果设备一段时间没有打开,那么下次对其进行更新时,它会跳过一系列发行版本,因此它必须能够进行直接的版本跳过更新。更新部署的结果是确保任何给定更新都可以应用在任何给定过去版本之上的噩梦。此外,由于使用git散列来标识版本而不是版本号,因此目前尚无法对版本进行字典式比较以促进增量更新。 我当前不支持的一个新要求是,将存在一些必须在管理服务器端配置的每设备配置选项(键/值对)。我不介意以某种方式将这些每设备选项以与软件更新相同的HTTP请求提供给设备(也许我可以将其封装在HTTP标头/ Cookie中),尽管我不太担心,因为我可以始终使其成为单独的HTTP请求。 …

3
在松散耦合的微服务架构中,如何跟踪依赖关系?
现代程序中流行的高级体系结构选择是基于REST的微服务系统。这具有几个优点,例如松耦合,易于重用,对可用技术的限制有限,高可伸缩性等。 但是我预见到的这种架构中的问题之一是对应用程序依赖项的可见性不佳。例如,假设我有一个应用程序,该应用程序每天使用一组REST调用。该应用程序还使用第二组REST调用,但每季度仅使用一次。如果我要扫描过去一周的日志,我会看到所有的每日校准数据,但可能看不到季度调用。当需要重构时,每季度调用的中断风险很高。 可以使用什么模式或工具来减少这种风险,并提供对松散耦合体系结构的依赖性的更大可见性?

1
当对象仅使用部分接口时,如何构造接口?
我有一个项目,其中有两个类都需要更新同一张表的数据库访问对象。框架和项目的约束使得它无法合并这两个类。我在下面创建了一个案例,展示了如何进行设置。A类需要能够更新和读取记录,而B类需要能够更新和删除记录。 如果我按原样使用这些类,则可以正常工作,但是我对每个类都需要不使用它的功能这一事实感到怀疑。例如,为了使用类A,我必须将实现删除功能的dao传递给它,即使它永远不会被调用。同样,我必须将实现read函数的dao传递给B类,但它永远不会被调用。 我考虑过通过继承其他接口(IReadDao,IUpdateDao,IDeleteDao是将要继承的dao)来实现该方法,但是这种方法对于每种功能组合(IUpdateAndRead,IReadAndDelete,IReadAndUpdate ... ) 我想为dao使用接口,因为我不想将应用程序与数据库耦合。有没有一种模式或方法可以实现任何人都知道的我想要的东西?提前致谢。 class IDao { void update(ModelDao model); void delete(String guid); ModelDao read(String guid); } Class A { private IDao dao; public A(IDao dao) { this.dao = dao; } public void doStuff() { ModelDao model = new ModelDao(); ... dao.update(model); } public void readThenDoSomething(String id) { …

2
有效跟踪从开发人员到产品的配置更改
这个问题以Spring Boot服务为例,但是可以是任何技术。 假设以下内容: 环境(dev / QA / prod)由不同的团队拥有。这意味着开发人员不能访问生产配置。 配置(例如application.properties)是外部化的,即不是二进制文件的一部分 相同的二进制文件/程序包(例如,service.jar)部署在每个环境中,并由自动部署控制 虽然对二进制工件(service.jar)的更改会自动传播到每个环境,但是对配置的更改仍需要手动干预,这不可避免地最终导致在每个环境中失去同步。 例如,假设开发团队在其环境中向application.properties添加了一些键值对。记录这些新密钥的最佳方法是什么,以便在操作团队中进行部署时,他们确切知道要添加哪些密钥,从而将启动新服务并由于缺少密钥而看到新服务失败的风险降到最低? 我知道将涉及手动步骤,但我想知道人们如何处理此问题并找到最有效的方法。

7
在Scrum每日站立会议中进行与非签到相关的讨论是否可以接受?
我希望人们会给我一个潜在的明显问题。我曾在许多每天召开Scrum会议的组织中工作。一些组织确实严格只使用Scrum进行签入(“三个问题” –您昨天做了什么,您今天在做什么,您是否有任何阻止者?),而另一些组织则倾向于使用其他常规服务。公告或详细的技术讨论。 我已经听到过这样的争论,例如在本文中,允许像这样与非签到相关的讨论是一个错误-Scrum会议不应用于Scrum Master的一般公告,技术讨论等。 我从中看到的主要危害是会议的持续时间可能超过必要的时间(被迫参加与我无关的细节讨论很烦人)。 显然,与整个团队无关,不属于“三个问题”的讨论不应该成为直言不讳的一部分。但是,如果还有其他与整个小组相关的公告,无论如何都需要进行讨论,那么在那时(而不是在单独的会议或电子邮件中)进行讨论是否有害?

2
增加一组有限的选项;API重大变化?
以一个HTTP API端点为例,它发出以下响应模型: { "type": "Dog", "name": "Jessi", ... } 该type领域已经在文档中被描述为一个Dog,Cat或Fish。 例如Rat,是否将添加新选项视为API的重大更改? 将选项添加到有限列表(开发人员可以打开该列表)是否被视为对API的扩展或修改?
9 rest  api  api-design  json 

2
如何读取功能性JavaScript代码?
我相信我已经学到了JavaScript中的一些/许多/大多数基本的函数式编程基础。但是,我在阅读功能代码(甚至是我编写的代码)时遇到了麻烦,并且想知道是否有人可以给我提供任何可以提供帮助的指针,技巧,最佳实践,术语等。 使用下面的代码。我写了这段代码。它旨在在say {a:1, b:2, c:3, d:3}和之间分配两个对象之间的相似度百分比{a:1, b:1, e:2, f:2, g:3, h:5}。我针对Stack Overflow上的这个问题生成了代码。因为我不确定海报要问的相似度百分比,所以我提供了四种不同的相似度: 在第二个对象中可以找到的第一个对象中键的百分比, 在第二个对象中可以找到的第一个对象中值的百分比,包括重复项, 在第二个对象中可以找到的第一个对象中的值的百分比,不允许重复,并且 在第一个对象中可以找到的第一个对象中{key:value}对的百分比。 我以合理的命令性代码开始,但是很快意识到这是一个非常适合函数式编程的问题。尤其是,我意识到,如果我可以针对上述四种策略中的每一种提取出一个或三个函数,这些策略定义了我要比较的特征类型(例如键或值等),那么我可能会能够将其余的代码减少(请原谅文字的打法)为可重复的单元。要知道,保持干燥。所以我改用函数式编程。我为结果感到非常自豪,我认为它相当优雅,并且我认为自己做得很好。 但是,即使我自己编写了代码并在构造过程中理解了代码的每个部分,当我现在回头看时,我仍然对如何读取任何特定的半行以及如何读取都感到困惑。 “抱怨”任何特定的半行代码实际上在做什么。我发现自己制作了一些易用的箭头,将不同的部分连接起来,这些部分很快就会变成一团意大利面。 因此,谁能告诉我如何以简洁明了的方式“阅读”一些更复杂的代码,这有助于我对正在阅读的内容的理解?我猜最吸引我的部分是连续有几个粗箭头的部分和/或连续有多个括号的部分。同样,在它们的核心处,我最终可以弄清楚逻辑,但是(我希望)有一种更好的方法可以快速,清晰,直接地“接受”一系列功能性JavaScript编程。 随意使用下面的任何代码行,甚至其他示例。但是,如果您需要我的一些初步建议,以下是一些建议。从一个相当简单的开始。在代码的末尾,有一个作为参数传递给函数:obj => key => obj[key]。一个人如何阅读和理解呢?较长的示例是从一开始就提供的一项完整功能:const getXs = (obj, getX) => Object.keys(obj).map(key => getX(obj)(key));。最后map一部分让我特别感兴趣。 请注意,目前我不是在寻找Haskell或符号抽象符号或currying的基础知识等的参考。我要寻找的是英文句子,我可以在看着一行代码时默默地开口。如果您有专门解决此问题的参考书,那很好,但是我也没有在寻找答案,我应该去读一些基础教科书。我已经做到了,我得到了(至少是很大一部分)逻辑。还要注意,我不需要详尽的答案(尽管可以尝试这样的尝试):即使是简短的答案,也提供了一种优雅的方式来读取一行特定的否则麻烦的代码,将不胜感激。 我想这个问题的一部分:灿我甚至线性阅读的功能代码,你就知道,左到右,上到下的?还是被迫在绝对不是线性的代码页上创建意粉般布线的心理图景?而且,如果必须这样做,我们仍然必须阅读代码,那么如何获取线性文本并连接意大利面呢? 任何提示将不胜感激。 const obj1 = { a:1, b:2, c:3, d:3 }; const obj2 = { a:1, …

3
在敏捷中,如何使用严格的管理框架(例如TFS在线)计划和分配项目开始时的基本基础架构任务?
在这里,我正在确定和估计一个相对较小的新软件开发项目。我已经遍历了客户建议的用户案例,并针对每个案例放置了任务,并提供了估算和一些简短的注释,说明如何完成任务。有验收标准。所有人都应该对世界有益。 在查看我计划的工作时,我意识到缺少一些东西。只需设置一些我们可以固定功能的东西,这是最初的支出。属于所有用户故事的事物,而不是一个特定的用户故事。 例如,此应用程序的一部分是解析XML的服务。从用户的角度来看,有一些特定的故事,根据XML的内容,需要做不同的事情。实际上,编写XML解析器(查找文件的位,读取文件并提取相关数据,然后再决定如何处理内容)是所有这些故事的一部分。就像使用安装程序等将其包装在Windows服务中一样。这是以开发人员为中心的任务,与用户没有直接关系。 该特定应用程序的另一个相关示例是获取并重写一段不良的旧代码,这对此应用程序的功能很有用。同样,这对用户没有立即的结果,但这是必要的工作。在针对用户故事的项目计划中,如何计划和执行这项工作? 我已经看到人们通过写用户故事“作为开发人员,我想...”来解决这个问题,但是正如其他地方所讨论的,这不是用户故事。是开发人员。 我正在为此寻求一个具体的答案,以帮助我(和其他人)使用严格的管理框架(例如TFS在线)来计划项目。这些往往不具有编写“利益相关者故事”或其他模糊的元解决方案的功能,这些解决方案在Scrum团队如何在计划会议中解决基础架构任务的答案中提到。

3
如何将软件修改为实时?[关闭]
已关闭。这个问题需要更加集中。它当前不接受答案。 想改善这个问题吗?更新问题,使其仅通过编辑此帖子来关注一个问题。 3年前关闭。 首先,我想提到我是实时系统编程的新手,这就是为什么我不确定我的问题是否正确的原因。抱歉,我需要一些帮助 简而言之,问题是: 如何实施硬实时软件以确保其在硬期限内完成?是否有必要使用某些QNX功能?还是足以为Linux编写它,移植到QNX并默认为实时? 完整问题: 我们已经针对Linux,Windows,Android和QNX实现了一些具有进程间通信的复杂跨平台多进程软件。编程语言是C ++,我们使用Boost和其他库的planty。我们的软件可以很好且快速地完成工作,但它仍是原型。出于生产目的,我们需要实时进行操作,因为某些功能非常重要,因此某些功能必须实时且非常可靠,并且使用我们软件的人员的安全性可能取决于这些功能。它们的工作速度非常快-高达数百毫秒。但是由于这个事实,我不确定我们的系统是否是实时的(我对吗?)。 因此,存在一个主要问题:如何将我们的软件修改为实时的?我已经用谷歌搜索了很多,但是我仍然不知道该怎么做。 有关平台的一些其他信息:Linux和Windows,我们目前仅用于测试目的。Android-我们仍未决定是否需要它。QNX-是我们生产的目标操作系统。我想我的下一个问题的答案是“否” :)但是,是否有可能实施跨平台的实时软件(用于实时OS(RTOS)以及用于通用OS(GPOS))? 可能我们需要努力只为QNX实施所有实时功能吗?但是我还是不知道该怎么做。有人可以阐明这个问题吗?
9 c++  real-time 

3
如何存储大量的_structured_数据?
该应用程序将连续(大约每秒)收集用户的位置并将其存储。 该数据是结构化的。在关系数据库中,它将存储为: | user | timestamp | latitude | longitude | 但是,数据太多。每个用户每天将有60×60×24 = 86,400条记录。即使有1000个用户,这也意味着每天86,400,000条记录。 每天不仅有86,400,000条记录。因为这些记录将被处理,并且它们的处理后的版本也将被存储。因此,将该数字乘以大约2。 我打算如何使用数据 本质上,我计划对位置数据进行更粗粒度的处理,以便于使用。那是: 对接收到的带有时间戳的数据进行排序。 依次浏览此列表,确定位置是否发生了重大变化(通过查看经度和纬度发生了多少变化) 将不重要的位置更改表示为输出中的单个条目(因此,输出是位置数据的粗粒度版本)。 通过要求更大的纬度和经度更改以进行重大更改,对输出进行迭代处理。因此,从先前输出产生的输出将更加粗糙。 根据需要重复整个过程。 汇总一系列分辨率并将其发送给用户。另外,存储数据的所有分辨率以供以后使用。 我应该用什么来存储这些数据?我应该使用关系数据库还是NoSQL解决方案?设计此应用程序时,我还应考虑哪些其他事项?

2
这是C ++中基于“ pImpl”的类层次结构的好方法吗?
我有一个我希望将接口与实现分开的类层次结构。我的解决方案是有两个层次结构:接口的句柄类层次结构和实现的非公共类层次结构。基本句柄类具有一个指向实现的指针,派生的句柄类将其强制转换为派生类型的指针(请参见function getPimpl())。 这是我对带有两个派生类的基类的解决方案的草图。有更好的解决方案吗? 文件“ Base.h”: #include <memory> class Base { protected: class Impl; std::shared_ptr<Impl> pImpl; Base(Impl* pImpl) : pImpl{pImpl} {}; ... }; class Derived_1 final : public Base { protected: class Impl; inline Derived_1* getPimpl() const noexcept { return reinterpret_cast<Impl*>(pImpl.get()); } public: Derived_1(...); void func_1(...) const; ... }; class Derived_2 …
9 design  c++  c++11 

2
如何在用Java编写的API中封装内部类?
我们必须编写一个库。自然地,它应该只具有非常小的API(尽可能广泛且尽可能小的)。库的内部结构有些复杂。因此,它们需要结构化。 对于结构化,我目前看到两种方法: 1.使用包装。 优点:库可以结构整齐。一切都在其位置。 缺点:通过包边界使用类需要公共类,因此扩展了整个库的API。 2.使用静态内部类,全部在一个包中。 优点:只需要很少的公共事物(类,方法等)。 缺点:类被隐藏只是为了构造它们。这将是使用大量静态内部类的极少数用例之一。开发人员不习惯于此,可能会忽略它们。 有没有更好的方法来在结构良好的库中实现小型API? 编辑:我忘了提及:这是一个Android库。因此,没有java9。

2
是什么使迭代器成为设计模式?
我一直在想,与其他类似构造相比,使Iterator变得特别的原因是什么,并且使“ 四人帮”将其列为设计模式。 迭代器基于多态性(具有公共接口的集合的层次结构)和关注点分离(对集合的迭代应独立于数据的结构方式)。 但是,如果我们将集合的层次结构替换为例如数学对象的层次结构(整数,浮点数,复数,矩阵等),而迭代器由代表这些对象的某些相关操作(例如幂函数)的类替换,该怎么办?类图将是相同的。 我们可能会发现更多类似的示例,例如Writer,Painter,Encoder和更好的示例,它们的工作方式相同。但是,我从未听说过其中任何一种称为设计模式。 那么,什么使Iterator特别呢? 是否因为需要可变状态来将当前位置存储在集合中而变得更加复杂,这是事实吗?但是,通常不认为可变状态是理想的。 为了阐明我的观点,让我举一个更详细的例子。 这是我们的设计问题: 假设我们有一个类的层次结构,并且在这些类的对象上定义了一个操作。每个类的操作接口相同,但实现方式可以完全不同。还假定对同一对象(例如具有不同的参数)多次应用该操作是有意义的。 这是解决我们设计问题的明智解决方案(实际上是迭代器模式的概括): 为了分离关注点,不应将操作的实现作为功能添加到原始类层次结构(操作数对象)中。由于我们想在同一个操作数上多次应用该操作,因此它应该由一个持有对该操作数的引用的对象来表示,而不仅仅是由函数表示。因此,操作数对象应提供一个函数,该函数返回表示操作的对象。该对象提供执行实际操作的功能。 一个例子: 有一个基类或接口MathObject(愚蠢的名字,我知道,也许有人有更好的主意。)与派生类MyInteger和MyMatrix。对于每个MathObject操作,Power都应定义一个允许计算平方,立方等的运算。所以我们可以写(用Java): MathObject i = new MyInteger(5); Power powerOfFive = i.getPower(); MyInteger square = powerOfFive.calculate(2); // should return 25 MyInteger cube = powerOfFive.calculate(3); // should return 125

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.