首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

指纹锁就安全了?防火防盗还得防AI

  • 24-03-05 01:41
  • 2834
  • 10306
blog.csdn.net

640?wx_fmt=jpeg

 

整理 | 一一

出品 | AI科技大本营(ID:rgznai100)

如何挑战百万年薪的人工智能

 https://edu.csdn.net/topic/ai30?utm_source=csdn_bw

 

 

近日,你应该看到了社交媒体上对于网站 ThisPersonDoesNotExist.com,生成无数不存在人脸的铺天盖地的消息,以及杨幂换朱茵的假脸图像。一方面,这说明,AI 技术的火正从专业人士那里不知不觉发展到了频繁上热搜的时期,但另一方面强势的 AI 技术发展带给了大众更大的恐慌情绪。

 

640?wx_fmt=gif

(有人用 deepfake 将朱茵在《射雕英雄传》中黄蓉的形象,换成了杨幂的脸,看上去毫无违和感)

 

从假人脸、假人声到假消息,AI 利用来自人类世界的数据集,正在创造一个以假乱真的仿真世界。

 

从人类的视角来看,AI 技术带来的这些前所未有的创造力是一种威胁,是为“假”,但换个角度,AI 正在创造的“仿真”人类信息世界,可能正给人们带来在面对未来时更大的困惑和不安全感。

 

在这里要重申:AI 正在创造一个独特的虚拟(虚假)信息世界。

 

除了众所周知的 Deepfake 这样的换脸技术外,今天要介绍的是与人们息息相关的指纹,它被广泛应用于指纹锁、手机、安检等应用场景中,有极高的安全等级。但如今指纹也开始能被 AI 技术“复制”了,由 AI 合成的指纹能轻松骗过识别的扫描仪。

 

这个叫 DeepMasterPrints 的系统,确实像是跟 Deepfake 来自同一个世界,在由 AI 创造太过逼真的事物上,业内人士一般都喜欢加个前缀“Deep”。

 

DeepMasterPrints 系统由纽约大学工程学院的 5 位研究人员开发,其研究于去年 10 月在洛杉矶举行的生物测量学会议上发表,主要可以用人工智能来制作虚假的指纹,它可以以假乱真,轻松“骗过”生物识别扫描仪(或人眼)。

 

研究人员称,DeepMasterPrints 在一个系统中复制了 23% 的人类指纹部分,错误率为千分之一。而当错误匹配率达到百分之一时,DeepMasterPrints 能在 77% 的情况下模拟真实指纹骗取扫描仪的“信任” 。

 

640?wx_fmt=png

左图是真实指纹,右图为 AI 合成指纹

 

这些合成指纹在“骗”过存有许多指纹的系统时可能很有效(不同于你手机中的指纹记录,它可能只记录了几个数字),DeepMasterPrints 开发的工具进行运行几个假指纹,通过系统查看是否有任何指纹与任何用户账户匹配。攻击者可能通过反复试验获得更多成功的机会,类似于黑客对密码进行暴力或字典攻击的破解方式,不是通过系统运行数百万流行密码的软件。

 

具体而言,其背后的技术原理是,通常研究人员采用两种生成对抗网络 GAN 组合在真实图像中使用,其中一个神经网络,使用公开、可用的指纹图像,训练神经网络识别真的指纹图像,然后用另一套神经网络,训练创建生成伪造指纹。

 

研究人员解释,可以将第二个神经网络的假指纹图像输入第一个神经网络中以测试仿真程度。随着时间的推移,第二个神经网络则会“学习”生成逼真的指纹图像,最终骗过人眼和扫描仪。

 

DeepMasterPrints 正是利用了生物识别指纹系统中的两个缺陷。首先,大多数指纹识别仪器在扫描时不会对整个指纹进行扫描,而只是对指纹的一部分上进行匹配;其次,多数设备允许用户提交多个指纹图像,匹配其中任何一部分,便可以确认用户身份。这使得由 AI 伪造的指纹更容易骗过指纹扫描仪。

 

640?wx_fmt=png

带有训练网络的潜在变量演化。左边是 CMA-ES 的高级概述,右边的方框表明如何计算潜在变量。

 

这样一个系统是如何创建的?根据论文描述,为了开发 DeepMasterPrint,研究人员将生成器的潜在变量演化为最优值。生成器的输入称为潜在变量,因为它们对网络输出的影响只能通过观察到的图像来进行理解。由于网络以 100 个潜在变量作为输入,那最优解是 100 维空间中的一个点。

 

 

如上图所示,LVE(Latent Variable Evolution,潜在变量演化技术) 对这些点进行采样,将它们转换为图像,然后对图像进行评分,以了解最佳点随时间的分布情况。这些最佳点是 DeepMasterPrint 的基因型,然后可以映射到图像上。

 

 

LVE 可以使用任何进化算法(或其他随机全局优化器)来搜索潜在空间。进化算法不需要梯度,因此这是黑盒优化的理想方法。在这个域中,匹配器可以报告匹配了多少身份(不同的指纹)以及相应匹配率,至于如何得到这些结果的却并不提供任何信息。

 

梯度没有显示 DeepMasterPrint 的哪个像素效果最好或最差。由于 LVE 的适应度得分是身份匹配的数量,因此适应度景观(fitness landscape)是不连续的。由于卷积网络的层次性,潜在变量也是不可独立分离的。

 

(论文传送门:https://arxiv.org/pdf/1705.07386.pdf)

 

研究人员的这篇论文像是给了黑客破解指纹锁的密码,但他们告诉 Gizmodo,如果没有验证生物识别是否来自真人,很多这些对抗性攻击都有可能发生,希望他们的研究能加强指纹安全工作,推动生物识别传感器中的活体检测。

 

不过,AI 技术的两面性在于道高一尺魔高一丈,不知道潘多拉魔盒完全打开后,还会如何“解锁”当下的物理世界,给人类更大的意想不到的震撼。

 

相关链接:

https://www.engadget.com/2018/11/16/ai-fingerprints-biometric-scanners/

https://www.theguardian.com/technology/2018/nov/15/fake-fingerprints-can-imitate-real-fingerprints-in-biometric-systems-research?CMP=fb_a-technology_b-gdntech

 

(本文为 AI科技大本营整理文章,转载请微信联系 1092722531)

 

群招募

 

扫码添加小助手微信,回复:公司+研究方向(学校+研究方向),邀你加入技术交流群。技术群审核较严,敬请谅解。

640?wx_fmt=jpeg

推荐阅读:

  • 2018 Python开发者大调查:Python和JavaScript最配?

  • 拿下中科大的计算机课程全靠它了!

  • 访问量最高超7百万的Stack Overflow问题竟然是...

  • 给老婆写的Python教程

  • “软件外包城”下的马鞍山 | 程序员有话说

  • 神操作!程序员如何拿下硅谷顶级公司 200 万年薪?!

  • 可怕!9岁男孩为买任天堂游戏机,竟然...

  • 云评测 | OpenStack智能运维解决方案 @文末有福利!

  • 月入5万,程序员夫人们过上"贵妇"生活了吗?

                         640?wx_fmt=png

点击“阅读原文”,查看历史精彩文章。

作者:京东物流 冯志文

背景

上周开发了一个需求,发现一个历史功能,从产品和技术代码的角度看,将简单的事情变得复杂。这一经历再次深化了我对一个核心理念的认识:简化复杂性是产品设计和软件开发中永恒的挑战。我们必须不断努力,将复杂的逻辑转化为直观、易用的用户功能,并将冗长、难以维护的代码结构变为简洁、效率高的形式。

在《人月神话》中作者提到,软件开发的复杂度可以划分为本质复杂度和偶然复杂度。本质复杂度它是一个客观的东西,跟你用的工具、经验或解决渠道都没有任何关系。而偶然复杂度是因为我们在处理任务的时候选错了方向,或者使用了错误的方法。

作为工程师,我们的追求不仅仅局限于代码的编写。更深层次的,我们探索的是如何对抗软件本身产生的复杂度,如何将繁杂的需求转化为简洁、优雅的解决方案。

不单单是程序员,任何化繁为简的能力才是一个人功力深厚的体现,没有之一。越简单,越接近本质。这个“简单”指的是整体的简单,而不是通过局部的复杂让另一个局部简单。

附:需求案例复杂点 1)业务产品设计方面:Promise 业务类型(比如生鲜时效、航空时效、普通中小件时效等)与单据类型、作业类型之间存在一系列复杂的转换关系。但这几个类型本质是一样的,没必要转换,术语统一,对业务使用来说也简单。 2)技术代码方面(组内同学CodeReview发现的):代码方法“副作用”(side effect),即方法除了返回值之外,还通过修改某些外部状态或对象来传递信息。比如filterBusinessType方法的主要作用是返回一个int类型的值,但它也修改了入参的response对象作为一个副作用,外部链路会使用reponse对象属性值。并且代码内部调用链路复杂,对于新人来说成本较高。为了确保清晰理解这些关系,并有效地进行代码维护,特意对这些关系及代码链路进行了详细的梳理。

一、为什么要简单?

为什么我们要追求简单性?不应该是复杂,才能显得技术牛吗?

对应简单,各有个的说法,个人理解如下:所见即所得

1.比如你架构图别人一看就明白这是干什么的,系统之间如何交互,模板之间如何交互。

2.比如定义API 别人一看文档就明白功能职责,请求入参,出参含义,有基本的计算机知识人都能看明白,这叫所见即所得。

3.比如新人在1周左右就能快速更改代码,而不是要记住代码各种注意事项,尽可能减少用户的学习曲线和理解成本

接下来从本次需求的复杂案例着手,引入自己的一些思考

二、案例详细

1)产品设计

1.1)现状

Promise业务类型 > 单据类型 > 作业类型 各种转换关系,如下图

1.业务类型 转换单据类型(1 VS 1)







1.单据类型 转换为 作业类型

根据单据类型找到 仓、干支线、本地 作业类型







1.仓&干支线&本地 作业类型

仓库







干支线







本地





1.2)思考点

1.一对一映射的简化

“业务类型&单据类型 其实是1V1映射”,这表明系统当初设计时考虑了业务类型和单据类型之间的直接关联。如果这种一对一的关系确实存在,那么单据类型可能是一个冗余的概念,因为每个业务类型已经隐含了单据类型的信息。简化模型,去除冗余的单据类型,可以减少系统的复杂性,并可能简化数据库设计和代码实现。

1.概念统一

“作业类型本质还是业务类型”,这意味着在不同的上下文中可能使用了不同的术语来描述相同的概念。在编码和产品设计中,使用统一的术语可以减少混淆,提高团队成员之间的沟通效率,并使得新成员更容易理解系统。

1.维度划分

将业务类型进一步细分为“仓、干支线、本地维度”,这表明系统有不同的操作维度或者分类标准。这种维度划分有助于在不同层面上组织和处理业务逻辑。

2)代码问题

2.1)内部链路太长

业务类型逻辑入口之一:getBusinessTypeInfoForAll > getBusinessTypeInfo > getOrderCategoryNew > obtainOrderCategoryByCode > filterBusinessType





在我们的代码库中,上面关键的五个方法被多个调用入口所使用,这种情况使得管理这些入口变得极为棘手。由于调用点的广泛分布,理解代码的影响范围变得复杂,难以一目了然地掌握。此外,这种做法也显露出我们的代码缺乏清晰的分层架构。这一原则的缺失,不仅使得现有代码难以维护,也给未来的功能扩展和迭代带来了不必要的复杂性和风险。

2.2)副作用

在Java 编程语言中,术语“副作用”(side effects) 指的是一个函数或表达式在计算结果以外对程序状态(如修改全局变量、改变输入参数的值、进行I/O 操作等)产生的影响。



如下filterBusinessType方法的主要作用是返回一个业务类型int类型的值,但它也修改了传入的response对象的bulkOrder值作为一个副作用。在外面链路使用了bulkOrder属性值做逻辑判断



副作用问题:在filterBusinessType方法中如果是在response之前return了数据,从方法角度看不出问题,但整个链路会出现问题。

❌错误写法

scss
代码解读
复制代码
public int filterBusinessType(String logPrefix, OrderCategoryRequest request, OrderCategoryResponse response) { if(CommonSwitch.isSendPay362Switch()){ if(OrderInfoFilterUtils.sendPay362is3or4(sendpay)) { log.info("{} 业务类型=1(普通订单),命中医药低温时效,当sendpay362=3|4时", logPrefix); return BusinessSchemaEnum.forwardOrder.getBussinessType(); } } boolean bulkOrderFlag = isBulkOrder(logPrefix, dictId, storeId, request.getSkuQuantity(), request.getWeight(), response); }

✅正确写法

scss
代码解读
复制代码
public int filterBusinessType(String logPrefix, OrderCategoryRequest request, OrderCategoryResponse response) { /** * 切记:return必须在下面这行代码(判断是否满足大宗)后面,因为外面的getOrderCategoryNew * 方法会使用response.isBulkOrder()来判断是否大宗 * 你可以理解本filterBusinessType方法会返回业务类型,同时如果bulkOrderFlag是大宗为true,则入参response也需要赋值返回 response.setBulkOrder(true) */ boolean bulkOrderFlag = isBulkOrder(logPrefix, dictId, storeId, request.getSkuQuantity(), request.getWeight(), response); if(CommonSwitch.isSendPay362Switch()){ if(OrderInfoFilterUtils.sendPay362is3or4(sendpay)) { log.info("{} 业务类型=1(普通订单),命中医药低温时效,当sendpay362=3|4时", logPrefix); return BusinessSchemaEnum.forwardOrder.getBussinessType(); } } }

详细代码链路如下:

scss
代码解读
复制代码
//入口方法1 public OrderCategoryResponse getOrderCategoryNew(OrderCategoryRequest request) { response = obtainOrderCategoryByCode(request); //关注这行代码,使用了response.isBulkOrder()值 int promiseSchemeType = getPromiseSchemeType(request.getLogPrefix(), false, response.isBulkOrder(), response.getBusinessId()); } //方法2 private OrderCategoryResponse obtainOrderCategoryByCode(OrderCategoryRequest request) { filterBusinessType(logPrefix, request, response); } //方法3 public int filterBusinessType(String logPrefix, OrderCategoryRequest request, OrderCategoryResponse response) { /** * 切记:return必须在下面这行代码(判断是否满足大宗)后面,因为外面的getOrderCategoryNew方法会使用response.isBulkOrder()来判断是否大宗 * 你可以理解本filterBusinessType方法会返回业务类型,同时如果bulkOrderFlag是大宗为true,则入参response也需要赋值返回 response.setBulkOrder(true) */ boolean bulkOrderFlag = isBulkOrder(logPrefix, dictId, storeId, request.getSkuQuantity(), request.getWeight(), response); if(CommonSwitch.isSendPay362Switch()){ if(OrderInfoFilterUtils.sendPay362is3or4(sendpay)) { log.info("{} 业务类型=1(普通订单),命中医药低温时效,当sendpay362=3|4时", logPrefix); return BusinessSchemaEnum.forwardOrder.getBussinessType(); } } } //方法4 private boolean isBulkOrder(String logPrefix, int dictId, int storeId , int skuQuantity, BigDecimal weight, OrderCategoryResponse response) { ...... if (loj.getOrderItemsCount() <= skuQuantity || loj.getOrderWeightLimit().compareTo(weight) < 0) { //关注这行代码,改变了入参的response对应bulkOrder值 response.setBulkOrder(true); return true; } return false; }

2.3)思考点

思考点1:代码链路太长

内部代码链路太长是一个常见的维护问题,通常源于缺乏良好的模块化和抽象设计。以下是一些思考点:

1.避免过度抽象:虽然抽象可以帮助简化代码,但过度抽象反而可能会增加复杂性。确保你的抽象层次是合理的,并且每个抽象都有明确的目的和价值。

2.分层架构:将代码按照逻辑和职责分成不同的层次,每一层只对其下一层有依赖性,而不需要知道更深层次的实现细节。这样可以减少代码之间的耦合度,简化内部链路。

3.合并重复代码:如果发现多个方法都在执行类似的操作,尝试合并这些重复的代码段,创建一个通用的方法来处理它们。这样可以减少代码的总量,提高代码的可读性和可维护性。

4.团队共识和标准:与团队成员讨论并达成共识,制定一些编码标准和最佳实践,以便在日常开发中就能够遵循简洁性原则,避免产生新的长链路。

思考点2:副作用

1)注意事项

1.将副作用明确化:如果一个方法有副作用,应该在方法的名称、文档或使用方式中明确指出。这样可以帮助其他开发者更好地理解该方法的行为。

2.避免使用静态变量:静态变量可以被多个线程或方法共享,容易引起副作用问题。除非有明确的理由,否则应尽量避免使用静态变量。

3.完全消除副作用可能是不现实的,尤其是在需要与外部交互的应用程序中。关键是要理解副作用的存在,并采取合适的策略来管理和控制它们。

2)Java的设计约定鼓励我们遵循以下原则:

1.单一责任原则:每个方法或类都应该有单一的责任或功能。

2.最小惊奇原则:方法的行为应该符合预期,避免出现意外的副作用。

在这个例子中,filterBusinessType方法既返回一个整数结果,又更新了response对象,这违反了单一责任原则和最小惊奇原则。因为调用者可能会预期这个方法只会过滤业务类型,而不清楚它还会修改response对象。

3)如何规避这种现象

为了避免这种情况,可以采用以下几种策略:

1.分离关注点: 可以将获取业务类型和响应设置分离成两个不同的方法。这样,调用者就可以清晰地看到每个方法的职责。

arduino
代码解读
复制代码
public int filterBusinessType(String logPrefix, Request request) { // 过滤逻辑... int businessType=...; return businessType; } public void setResponseData(int filterResult, Response response) { // 根据过滤结果设置响应数据... response.setFilteredData(...); }

1.返回复合对象(上下文context) : 如果业务类型结果和响应数据是紧密相关的,可以考虑创建一个包含这两个信息的复合对象,并将其作为方法的返回值。

arduino
代码解读
复制代码
public FilterResultAndResponse filterBusinessType(Request request) { // 过滤逻辑... int result=...; Response response=new Response(); response.setFilteredData(...); return new FilterResultAndResponse(result, response); } class FilterResultAndResponse { private int filterResult; private Response response; public FilterResultAndResponse(int filterResult, Response response) { this.filterResult = filterResult; this.response = response; } // Getters and setters for filterResult and response }

总的来说,副作用有时候是不可避免的,但我们可以通过以上方法来规避和管理它们,写出更可靠、更易于维护的代码。

三、案例解决方案

回到本文开头说的产品和技术复杂性案例,考虑到产品和技术影响promise时效内核最底层逻辑范围。我是采取保守的策略:维持现状不变。这是因为对核心逻辑进行改动会带来广泛的影响和较高的风险,可能会牵扯到整个系统的稳定性和一致性。 然而,这并不意味着我们放任复杂性存在。相反,我做了以下两点改进措施:

1.增加注释和注意事项:在代码中添加详细的注释和注意事项,帮助团队其他开发者理解这部分代码的工作原理和潜在风险。这样可以降低新成员上手的难度,并且减少在维护或扩展时出现错误的可能性。

2.团队分享和知识传递:我将分享这个案例和相应的经验教训,向团队成员解释为什么在这个特定情况下我们选择了保持现状不变,以及如何在未来的需求、架构设计和代码编写中更好地管理复杂性和副作用。通过这种方式,我们可以共同学习和成长,避免在类似情况下重蹈覆辙。

这两种方法虽然不能立即简化复杂性,但它们可以提高代码的可读性和可维护性,减少长期的技术债务。同时,团队分享也能促进知识共享和团队协作,让大家知道什么是正确的做法,帮助我们在面对类似挑战时做出更明智的决策。

四、如何做到简单--思考点

KISS 原则是指在设计当中应当注重简约的原则。总结工程专业人员在设计过程中的经验,大多数系统的设计应保持简洁和单纯,而不掺入非必要的复杂性,这样的系统运作成效会取得最优;因此简单性应该是设计中的关键目标,尽量避免不必要的复杂性。

1)产品设计

作为技术从业者,我们常常需要从用户(无论是面向C端消费者还是面向企业内部的产品)的视角出发,来探讨产品设计中的简洁性原则。以下是我的一些观点,说的并不一定对,但愿能为您提供一些启发。

以用户为中心

•深入理解用户需求:深刻洞察用户的核心需求和痛点,用客观数据驱动决策,而不是单凭个人直觉。

•简化用户旅程:力求打造一个直观的用户旅程,尽量减轻用户的决策压力和学习负担。一个优秀的界面应该是清晰、易懂的,使用户能够毫不费力地完成所需任务。

减法设计

•在设计每一个功能环节时,我们需要反复自问:这个功能是否真正必要?如果它的缺失不会损害用户体验,那它很可能是多余的。

直观交互

•设计时应确保控件和操作逻辑能够自然地映射其功能,使用户能够直觉地理解产品的使用方法。

持续迭代

•不断地收集用户画像和分析用户反馈,将其作为产品设计迭代的重要依据,以此不断地精进和完善产品。

案例: 1、Alfred:这个我觉得根本无需介绍,神器,使用 macOS 的同学应该都知道。一句话来说就是,Alfred 是 macOS 上神级的效率应用,能够在实际操作中大幅提升工作效率。

2、1Password:使用 1Password 生成并管理密码后,就再也不用费心思去想密码的事情了,只需要记住 1Password 主密码就万事大吉。 原先你需要4步骤:比如1)打开浏览器 2)输入网站(或者打开收藏夹) 3)打开网站输入用户名密码 4)点击登录 使用1Password只需要 一步到位,自动打开浏览器登录相关页面

2)架构设计

不要跟风选择所谓高大上的技术,适合的才是最重要的。够用+1即可。什么意思呢,就是系统目前够用再往前走一步就可以了。至于这一步是什么?可能需要你在实践过程中,慢慢找到你认为比较合适。很多时候,我们系统架构引入一个新框架或者新技术,它本身带来的复杂性其实比你这个问题还要复杂。

简化架构也是提高技术稳定性的重要步骤。一个复杂的架构可能会导致系统的各个部分难以协同工作,从而影响系统的稳定性。因此,我们应该尽量采用简单的架构设计,使得各个部分可以更容易地协同工作。

案例:业务架构简单化-小件日历天数30天扩充到90天 复杂解法: 1)目前是根据业务的时效配置预计算好30天日历,依赖N个配置(仓-干支线-本地缓存等),在现有基础上,预计算90天日历。 2)缺点:牵扯数据预计算N个地方改造,并且增加了数据量的存储。改造排期长并且数据存储成本高 简单解法: 1)还是保持现有30天日历的算法。第31天以后的日历按照最后一天日历进行复制。如果日历计算命中集约地址(比如3天1送),过滤对应日历即可。 2)优点:代码改造工作量小,数据存储成本保持不变



案例2:技术架构简单化-避免过度使用技术栈 以缓存(本地、分布式缓存)为例,它的引入确实能显著提高系统的响应速度和效率。然而,这同时也带来了新的挑战,如数据一致性问题和缓存策略的选择。





1)数据一致性问题可能导致用户获取到旧的或不正确的信息。 2)而缓存策略的选择则需要在系统资源利用和数据时效性之间找到平衡点。 为了解决这些问题,我们可能需要引入更复杂的缓存失效策略, 1)如基于时间的失效、事件驱动的失效机制, 这些策略的引入和管理本身就增加了系统的复杂性,因此在设计缓存解决方案时,我们需要仔细权衡其带来的效率提升和潜在的复杂性增加,以找到最适合当前系统需求的平衡点。

3)最小API

对外 API 的设计决定了系统的可扩展性和兼容性。一个清晰、简洁且易于理解的 API 设计可以减少各种交互问题。编写一个明确的、最小的API是管理软件系统简单性的必要条件,我们向API消费者提供的方法和参数越少,这些API就越容易理解,就有更多的时间去完善这些方法,

将复杂的API设计简化为更易用、更直观的形式,以便用户能够更容易地理解和使用。

1.使用标准格式:遵循一致的命名约定、数据格式和错误处理机制。这将使API更加一致和易于使用。

2.API功能单一职责原则:在API设计中,单一职责原则也非常重要。如果一个API具有多个职责,那么它将变得复杂且难以维护。因此,建议将API拆分为多个简单的API,每个API只负责一个特定的职责。明确其功能和用途。这将有助于确保API具有清晰的职责划分,避免不必要的复杂性。

3.简化参数:尽量避免使用过多的参数,而是使用简单、易于理解的参数。如果必须使用多个参数,请确保它们之间有明确的关系。

4.提供简洁的文档:编写简洁明了的API文档,解释每个端点的功能、请求方法、参数和响应格式。确保文档易于阅读和理解,以便用户能够快速上手。

5.提供示例代码:为API提供示例代码,展示如何使用不同的请求方法和参数。这将帮助用户更快地掌握API的使用技巧。

在软件工程上,少即是多,一个很小、很简单的API通常也是一个对问题深刻理解的标志。

案例1:Promise适配M系统API 背景:M系统的时效是自己计算闭环的,promise是对外统一收口时效,在M系统时效业务线上,promise只是透传,不做任何时效逻辑 复杂解法: 1)每次M系统相关时效需求,下游M系统的API需要变更,promise也需要参与改造,改造点2个,第一个是从订单中间件xml获取M系统需要的参数。第二点把获取的参数调用M系统API透传 2)缺点:需求改造系统多,但都是转发适配,无核心逻辑,工作量耗时长,项目排期协调,沟通成本大 简单解法: 1)跟M系统沟通,M系统时效要的信息从X节点获取,promise把该节点的json信息全部透传给M系统,这样后期需求promise不参与改造, 2)优点:从promise角度来说新需求不用改造,从M系统角度来说时效自己闭环。这是双赢的局面,从全局来说,减少了链路的开发/联调/沟通/协调成本,整个项目交互效率提升了.



案例2:❌错误码设计---未传播错误码 案例:冷链外单无妥投时间,目前链路是ECLP---->Promise---->路由系统。但错误码是各自封装,没有把根本原因传播出去,而是各自加工,导致最终看到的原因跟真实的原因千差万别。 导致整个链路牵扯 业务方--->eclp研发---->promise研发---->路由研发---->路由网规业务同事 总共5个环节,如下图:





具体咨询链路如下 1、业务联系ECLP系统开发,告知系统异常提示如下





2、联系promise小蜜,小蜜根据订单查询调用纯配接口出入参:





3、promise联系路由,周知出入参 4、路由同事联系 网规,告知路线没配,信息如下:{"code":0,"data":[],"message":"派送范围维护->未查找到配置数据,请排查派送范围维护,派送地址:四川-达州市-宣汉县-龙泉土家族乡,产品:生鲜特殊次晨,生鲜特惠次晨,生鲜标快","tid":44845519852540539}



案例2: ✅错误码信息--传播错误码信息 1、如果api在翻译错误时,需要把底层根本原因返回上去,比如上面案例,把没有妥投日期的根本原因【派送范围维护->未查找到配置数据,请排查派送范围维护,派送地址:四川-达州市-宣汉县-龙泉土家族乡,产品:生鲜特殊次晨,生鲜特惠次晨,生鲜标快","tid":44845519852540539】周知 2、改造后链路 ECLP业务方---->路由网规业务同事 总共2个环节(改造前5个环节),因为界面提示错误信息,所见即所得,减少了中间环节。提升了业务效率,减少了研发内部中间环节的排查成本。





4)代码简单

编码简单化也是提高技术稳定性的有效方法。过于复杂的编码可能会导致错误和漏洞的出现,从而影响系统的稳定性。因此,我们应该尽量使用简单、清晰的代码。此外,我们还应该注重代码的可读性和可维护性,这样可以更容易地找到和修复错误。

1.遵循单一职责原则:每个函数或类应该只负责一个特定的任务。这样可以使代码更易于理解和维护,并减少错误的可能性。

2.避免冗余代码:尽量避免重复的代码。如果需要多次使用相同的代码块,请将其封装为函数或方法,以便在需要时调用。

3.使用注释来解释复杂的逻辑:如果代码中包含复杂的逻辑或算法,请使用注释来解释其工作原理。这可以帮助其他人更好地理解代码。

4.将长代码段拆分为多个小段:如果一个代码段很长,可以考虑将其拆分为多个小段,每个小段只做一件事情。这可以使代码更加清晰明了,并有助于调试和维护。

5.使用有意义的变量名和函数名:变量名和函数名应具有描述性,以便其他人可以快速了解其用途。



总之,编写简单的代码需要考虑多个方面,包括可读性、可维护性和可重用性等。

❌DUCC开关-复杂化 promise之前用的是ucc,后迁移到ducc,刚开始不清楚ducc本身就支持string类型的set封装赋值了,所以采用的是原始的String截取拼装方式,导致代码复杂





ini
代码解读
复制代码
private String jitEndOrderTimeBufferSwitch = ""; private Set jitEndOrderTimeBufferSwitchSet = new HashSet(); public void setJitEndOrderTimeBufferSwitch(String jitEndOrderTimeBufferSwitch) { log.info("jitEndOrderTimeBufferSwitch设置为:{}", jitEndOrderTimeBufferSwitch); this.jitEndOrderTimeBufferSwitch = jitEndOrderTimeBufferSwitch; Set tmpJitEndOrderTimeBufferSwitchSet = new HashSet<>(); if (StringUtils.isNotBlank(jitEndOrderTimeBufferSwitch)) { String[] jitEndOrderTimeBufferSwitchArr = jitEndOrderTimeBufferSwitch.split(","); tmpJitEndOrderTimeBufferSwitchSet.addAll(Arrays.asList(jitEndOrderTimeBufferSwitchArr)); } this.jitEndOrderTimeBufferSwitchSet = tmpJitEndOrderTimeBufferSwitchSet; log.info("jitEndOrderTimeBufferSwitchSet设置为:{}", jitEndOrderTimeBufferSwitchSet); }



✅DUCC开关-简单化

typescript
代码解读
复制代码
private Set<String> jitEndOrderTimeBufferSwitch = new HashSet<String>(); public void setJitEndOrderTimeBufferSwitch(Set<String> jitEndOrderTimeBufferSwitch) { this.jitEndOrderTimeBufferSwitch = jitEndOrderTimeBufferSwitch; log.info("jitEndOrderTimeBufferSwitch设置为{}", this.jitEndOrderTimeBufferSwitch); }

五、简单原则--践行中

我也是正在积极践行以下原则。虽然在实践中仍面临挑战,但正不断学习和改进

1)复杂(重复)的事情简单(工具)化

当我们面对重复而无差别的任务时,工具化的价值便凸显出来。引入合适的工具不仅简化工作流程,还能大幅提升效率。

对于复杂的业务逻辑,我们应致力于深入梳理和理解。详尽的文档是理解这些逻辑的钥匙,它能够将复杂性降低。

对于系统架构,我们应该梳理上下游依赖、交互、核心接口、业务场景、应急预案等,具备全局视图

对于技术密集的代码,充分的注释和示例案例是必不可少的,它们是简化理解过程的桥梁。

我们还应该将复杂的系统解构为小型、可管理的模块,这是一个将复杂事物简化的过程。

2)简单的事情标准化

一旦这些复杂的系统被拆分成多个简单的组件,我们就可以对每个组件进行定制化和标准化。

3)标准的事情流程化

这样的标准化模块,一旦定制完成,就能够形成一个简洁且固定的流程。这种流程化不仅为防止最糟糕情况的发生提供了保障,也使得任务能以统一和高效的方式运行。

4)流程的事情自动化

正如自动化测试所示,一旦流程化得以实施,自动化的基础便已铺垫。基于这一基础,我们可以将复杂的任务转化为自动化的操作,从而尽可能地减少手动干预,实现高效运作。

案例:行云部署发布上线 简单提效快 (引用《行云部署提效快,部署编排用起来》) 背景:为解决用户手动部署操作耗时高、分组多人工容易遗漏、对人依赖度高等痛点,2个以上分组,20个容器以上的应用,强烈推荐您使用【部署编排】功能,用户可灵活制定部署策略,实现从编译构建到实例部署的自动化运行,提高部署效率! 复杂-->简单-->标准-->流程-->自动化: 部署编排接入了丰富的原子,提供了部署策略、流量管理、编译构建等功能,可基于这些功能进行任务排布,形成一个独立的部署编排。部署时,只需执行此编排任务即可,解放双手实现自动化部署!同时部署编排支持多分组同时部署。





五、总结

1.复杂的事情简单化

2.简单的事情标准化

3.标准的事情流程化

4.流程的事情自动化

我们先踏出第一步化繁为简

简化复杂性不仅能在短期内提高开发效率和代码质量,也对产品和技术的长期价值产生深远影响

1.当我们考虑如何简化一个给定的任务的每一步时,这不并是在偷懒。相反,我们是在明确实际上要完成的任务是什么,以及如何更容易做到。

2.我们对某些无意义的新功能说“不”的时候,不是在限制创新,是在保持环境整洁,以免分心。

3.软件的简单性是可靠性的前提条件。这样我们可以持续关注创新,并且可以进行真正的有价值的事、长期的事。



本文旨在抛砖引玉,仅就偶然复杂度的议题,从产品与技术的角度,分享一些关于简单化的个人思考。希望这些初步的观点能激发更多精彩的思考和深入的实践。如果文中有任何不足之处,恳请各位不吝赐教,留言指正。谢谢大家的阅读和反馈!

注:本文转载自blog.csdn.net的AI科技大本营的文章"https://blog.csdn.net/dQCFKyQDXYm3F8rB0/article/details/87959240"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2492) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

101
推荐
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2025 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top