写出好代码的底层逻辑!

程序员安身立命的手艺就是写代码,可多少人知道如何才能写出好的代码呢?这几年也做过很多次的代码 CR,可好代码的标准在哪里呢?我们在做 CR 的时候,其实只是停留在代码的表面,主要是跟规范相关的点:命名、代码行数、代码写法、注释如何,这些其实都是可以用 ESLint 做规范去解决的,也很容易形成习惯。如果在这个基础上,每个人都做得不错的情况下,那到底什么是决定了代码的好坏呢?

在想这个问题之前,我们得想想,我们每天在做的编程这件事到底是什么?

什么是编程?

到底什么是编程,知乎上这样的一个问题,编程的本质是什么?

www.zhihu.com/question/20…[1]

1087f8c152fb0dd05015bd5725bc4aa9.jpeg
image

很多人都给了自己的回答,但我发现很多回答,要么长篇大论各种编程范式,要么说得特别抽象,比如下面的回答,毫无指导意义。

49eca8b093ea4829905fdbc5c932a117.jpeg
image

其实,很多人对于事物本质的理解,似乎都有道理,但是我们希望这个答案可以指导我们编程的行为,知道如何写出好代码,如果我们从这个角度来看,那这个问题的答案就需要非常地具体,可执行。

这么多解释里,陈皓老师的解释最为具体,当然他也是引用的国外大佬的研究结论,但分析得相当好。

de16735f2829884aa45b14a4df4b04b8.jpeg
image

简单的两个公式表达得比较透彻。

程序 = 算法 + 数据结构

算法 = 逻辑 + 控制

程序 = 算法 + 数据结构

我们在写程序的时候,本质都是在写算法和数据结构,而我发现,大家更多在写代码的时候关注的是算法,而非数据结构(数组、链表、栈、队列、树、图等),这有可能是大家写不好代码的第一个原因。就像 Linux 之父 Linus Torvalds 所说的,「糟糕的程序员关心代码。好的程序员关心数据结构和它们之间的关系」。

我们在刷算法题的时候,常常会发现,一个合适的数据结构可以极大地提升算法的效率,比如:

在 leetcode 上有一个 hard 的题目叫做:基本计算器

80f7629a34e4cc5ad44fed61771863f7.jpeg
image

这个题如果大家做过的话,其实有很多种解法,其中如果不用数据结构就会很复杂,如果用了栈这种数据结构,思路就会比较清晰,尤其是使用双栈这种数据结构。

因此,如果想成为一个会写代码的程序员,首先记住一点,「数据比代码更重要,代码的唯一目的是转换数据」。

有太多入行前端的同学,并不是计算机专业的,或者没有学过《数据结构》这门课,培训机构的教学也是以实战为主,一上来就开始写代码,长久的习惯导致代码的优化只停留在规范和格式上。

算法 = 控制 + 逻辑

除掉数据之外,代码就只剩下了算法,也就是我们日常写的流程代码,这部分依然可以拆分为控制和逻辑。这里要分清楚二者的关系:

控制 control

在任何语言中,都有对应的语法操作,比如 if、for、map、reduce 等,这些都是控制语句。还有其他程序执行的方式,并行还是串行,同步还是异步,以及调度不同执行路径或模块,数据之间的存储关系,模块的组织方式,是函数还是类,多线程、异步、服务发现、部署、弹性伸缩等,这些和业务逻辑没有关系的部分都是控制。

逻辑 logic

逻辑一般指业务逻辑,我们把真实的需求抽象成代码以后的部分,比如:我们在做用户登录页面的时候,点击登录按钮以后,去判断下账号和密码是否符合一定的规则,这就是业务逻辑。

业务逻辑就是问题的定义,对于排序问题来讲,逻辑就是“什么叫做有序,什么叫大于,什么叫小于,什么叫相等”?控制就是如何合理地安排时间和空间资源去实现逻辑。

两者的关系

  • Logic 解决问题,它决定了程序的本质复杂度,它是代码优化的下限。

  • Control 只影响效率,控制是代码优化的重点,需要尽量地降低复杂度。

  • Logic 和 Control 没有关系

  • Logic 和 Control 如果分开,代码更容易改进和维护。

74062ada26d8e6a48ae29c69820fd963.jpeg
image

底层逻辑:有效地分离 Logic、Control 和 Data 是写出好程序的关键所在

无论微观层面的代码,还是宏观层面的架构,无论是三种编程范式还是微服务架构,它们都在解决一个问题:分离控制和逻辑。

第一步:选好适合的数据结构。

第二步:做好业务逻辑的抽象(流程+模型)。

第三步:设计代码的控制过程(编程范式+设计模式)。

例子

function check_form_x() {
    var name = $('#name').val();
    if (null == name || name.length <= 3) {
        return { status : 1, message: 'Invalid name' };
    }
 
    var password = $('#password').val();
    if (null == password || password.length <= 8) {
        return { status : 2, message: 'Invalid password' };
    }
 
    var repeat_password = $('#repeat_password').val();
    if (repeat_password != password.length) {
        return { status : 3, message: 'Password and repeat password mismatch' };
    }
 
    var email = $('#email').val();
    if (check_email_format(email)) {
        return { status : 4, message: 'Invalid email' };
    }
 
    ...
 
    return { status : 0, message: 'OK' };
 
}

分离后:

// logic
var meta_create_user = {
    form_id : 'create_user',
    fields : [
        { id : 'name', type : 'text', min_length : 3 },
        { id : 'password', type : 'password', min_length : 8 },
        { id : 'repeat-password', type : 'password', min_length : 8 },
        { id : 'email', type : 'email' }
    ]
};
// control
var r = check_form(meta_create_user);

上面的例子,使用的是表驱动的方法,就是用一个 JSON 的数据结构来描述业务需求,再加一个控制函数,这样就可以很好的做到了数据、逻辑和控制之间的分离,同时还具备了很好的扩展性。

必备知识

代码解耦的方法有很多,要想写出好代码,需要掌握一些基础的理论知识。下面我列出了需要学习的一些必备知识。

  • 数据结构:可以通过学习专业的数据结构教材,或者刷算法题来精进。

  • 业务逻辑的抽象:多画业务逻辑图,流程图,明确完成一个逻辑必备的步骤,和可复用的模型。

  • 控制设计:

  • 编程范式

  • 命令式

    • 声明式

    • 函数式

    • 面向对象

  • 设计原则 SOLID

  • 单一职责原则(Single responsibility principle,SRP)

    • 开放封闭原则(Open–closed principle,OCP)

    • Liskov 替换原则(Liskov substitution principle,LSP)

    • 接口隔离原则(Interface segregation principle,ISP)

    • 依赖倒置原则(Dependency inversion principle,DIP)

  • 设计模式:有很多种设计模式,但是真正在前端常用的并不多,大家可以结合例子,系统性地学习。

    • 创建型模式:工厂、单例、创造者模式、原型模式。

    • 结构型模式:适配器模式、装饰器模式、代理模式、外观模式、桥接模式。

    • 行为型模式:观察者模式、策略模式、命令模式、迭代器模式、状态模式、责任链模式。

总结

上面所描述的写好代码的底层逻辑,同时给代码 CR 提供了一个比较好的方向,在做 codereview 的时候,可以多问问什么是逻辑,什么是控制,是否可以用数据结构来描述业务,什么样的数据结构是最适合的。

在设计代码的时候,也可以问一下自己上面的几个问题。把解耦慢慢变成自己的一个习惯,长此以往代码会具有更好的扩展性,可读性和稳定性。

作者:ES2049

链接:https://juejin.cn/post/7348463942404980777

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。
splice方法的底层逻辑可以通过以下步骤实现: 1. 首先,判断传入的起始索引(start)和删除元素的数量(deleteCount)是否超出数组的范围。 2. 根据传入的起始索引(start),计算实际的删除起始索引。如果传入的索引为负数,则从数组末尾开始计算。如果传入的索引大于数组长度,则将其设置为数组长度。 3. 创建一个空数组,用于存储被删除的元素。 4. 遍历数组,从删除起始索引开始,将被删除的元素添加到空数组中。删除元素的过程中,需要将数组中的元素向前移动,以填补被删除元素的空位。 5. 根据传入的新元素(如果有),将其插入到被删除元素的位置上。 6. 最后,返回被删除的元素组成的数组。 下面是使用JavaScript语言实现splice方法底层逻辑代码: ```javascript Array.prototype.spliceCustom = function(start, deleteCount, ...newItems) { const arr = this.slice(); // 复制原数组 const length = arr.length; let actualStart = start < 0 ? Math.max(length + start, 0) : Math.min(start, length); let removed = []; // 获取被删除的元素 for (let i = 0; i < deleteCount; i++) { if (actualStart + i < length) { removed.push(arr[actualStart + i]); } } // 将数组元素前移 let len = Math.max(length - deleteCount, actualStart); for (let i = actualStart; i < len; i++) { arr[i] = arr[i + deleteCount]; } // 插入新元素 let newItemsCount = newItems.length; let newLength = length - deleteCount + newItemsCount; for (let i = 0; i < newItemsCount; i++) { arr[actualStart + i] = newItems[i]; } // 截断数组 arr.length = newLength; return removed; }; ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值