今天看到有程序员发了一段很有意思的代码片段(veryefficientcode)。此代码已经传播开来,您可能已经在不同的平台上看到过它。关于这个话题有很多争论。一些人认为有更短(也许更好)的版本可以完成同样的工作。例如,我让ChatGPT重写了一个较短的版本,结果如??下:不是越短越好吗?老实说,我对原始版本的反应是,这到底是怎么回事?我自嘲地认为我可以在5分钟内使用地图或类似的巧妙技术来重构它。然而,喝了一杯咖啡后,我又看了一眼代码片段。我发现意图非常明确,具有讽刺意味的是地图版本需要更多时间来阅读。对于有经验的开发人员来说,较短的版本可能需要几秒钟才能弄清楚发生了什么。如果代码是几周前编写的,那么可能需要额外几分钟才能理解。原始代码有什么问题?虽然第一个版本的代码看起来简单明了,但它的缺点是无法将表现和业务逻辑结合起来。软件被设计为具有灵活性和适应性,此版本的代码使得将来更难进行更改。说它混合了表示和逻辑,我的意思是如果我们明天要显示一个红点(而不是蓝点),我们必须修改很多地方。除此之外,我想从一个与逻辑泄漏相关的小问题开始。您可能已经注意到,它多次重复percentage>x&&precentage<=y,我将提取一个函数以使其更具可读性:constisPercentageInRange=(number:number,low:number,high:number)=>数字>低&&数字<=高;如果我将百分比检查分成两个函数并在两个函数中绘制蓝点和白点并将结果排列在新的getPercentageRounds中,代码将如下所示:constgetBandByPercentage=(percentage:number)=>{if(percentage===0)返回0;如果(isPercentageInRange(百分比,0.0,0.1))返回1;如果(isPercentageInRange(百分比,0.1,0.2))返回2;如果(isPercentageInRange(百分比,0.2,0.3))返回3;如果(isPercentageInRange(百分比,0.3,0.4))返回4;如果(isPercentageInRange(百分比,0.4,0.5))返回5;如果(isPercentageInRange(百分比,0.5,0.6))返回6;如果(isPercentageInRange(百分比,0.6,0.7))返回7;如果(isPercentageInRange(百分比,0.7,0.8))返回8;如果(isPercentageInRange(百分比,0.8,0.9))返回9;返回10;};constdrawProgress=(percentage:number)=>{constband=getBandByPercentage(百分比);returnnewArray(10).fill("",0,band).fill("?",band,10);};constgetPercentageRounds=(percentage:number)=>{returndrawProgress(percentage).join("")}functiongetBandByPercentage将百分比映射到一个范围(或级别),drawProgress根据范围绘制圆点,使呈现更加灵活.我们可以提取蓝色和白色的点作为参数,让进度条更加灵活。此外,为了保持当前行为,我们可以使用当前值作为默认值:constdrawProgress=(percentage:number,done:string="",doing:string="?")=>{constband=getBandByPercentage(percentage);returnnewArray(10).fill(done,0,band).fill(doing,band,10);};然后你可以像这样在命令行中创建一个进度条:constgetPercentageRounds=(percentage:number)=>{returndrawProgress(0.3,'#','=').join("")}如果你想进度条越宽,可以传入更长的版本字符串,表示“已完成”和“正在做”:constgetPercentageRounds=(percentage:number)=>{returndrawProgress(0.3,'##','==').join("")}所以我们可以有不同的进度条,短的和长的,蓝色的和红色的。代码配置重构后,表现和逻辑分离。我不喜欢使用这么大的if-else块:constgetBandByPercentage=(percentage:number)=>{if(percentage===0)return0;如果(isPercentageInRange(百分比,0.0,0.1))返回1;如果(isPercentageInRange(百分比,0.1,0.2))返回2;如果(isPercentageInRange(百分比,0.2,0.3))返回3;如果(isPercentageInRange(百分比,0.3,0.4))返回4;如果(isPercentageInRange(百分比,0.4,0.5))返回5;如果(isPercentageInRange(百分比,0.5,0.6))返回6;如果(isPercentageInRange(百分比,0.6,0.7))返回7;如果(isPercentageInRange(百分比,0.7,0.8))返回8;(百分比,0.8,0.9))返回9;返回10;};正如MartinFowler的文章中所讨论的,在某些情况下,将“代码”拆分为配置文件是有益的。我们可以将这个百分比移动到波段映射中(或者甚至将bandConfig移动到JSON文件中):constbandConfig:BandConfig[]=[{range:[-Infinity,0.0],band:0,},{range:[0.0,0.1],band:1,},//...];那么getBandByPercentage可以简化为constgetBandByPercentage=(percentage:number)=>{constconfig=bandConfig.find((c)=>{const[low,high]=c.range;returnisPercentageInRange(percentage,low,high)});返回配置?.band;};将复杂性转移到配置文件后,getBandByPercentage函数就只有几行代码了。重用逻辑?让我再演示一个用例来说明拆分可以带来什么。现在假设我们想在WebUI中使用进度条-例如ProgressBar组件。导入drawProgress函数非常简单:constProgressBar=({percentage,}:{percentage:number;done?:string;doing?:string;})=>{return(<>{drawProgress(percentage).map((字符)=>({字符}))}>);};在页面上我们可以看到这样的东西:我们可以很容易地改变组件中的句点字符,让进度条更容易适应新的UI需求。总结最终结果可能不如原始结果“高效”。尽管如此,通过明确分离关注点(表示和业务逻辑,以及逻辑和配置),它可以更好地响应新需求。
