写出好代码的底层逻辑

写出好代码的底层逻辑

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

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

什么是编程?

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

www.zhihu.com/question/20…

image

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

image

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

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

image

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

程序 = 算法 + 数据结构

算法 = 逻辑 + 控制

程序 = 算法 + 数据结构

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

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

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

image

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

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

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

算法 = 控制 + 逻辑

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

控制 control

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

逻辑 logic

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

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

两者的关系

  • Logic 解决问题,它决定了程序的本质复杂度,它是代码优化的下限。
  • Control 只影响效率,控制是代码优化的重点,需要尽量地降低复杂度。
  • Logic 和 Control 没有关系
  • Logic 和 Control 如果分开,代码更容易改进和维护。

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 的时候,可以多问问什么是逻辑,什么是控制,是否可以用数据结构来描述业务,什么样的数据结构是最适合的。

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

文章转载于:https://juejin.cn/post/7348463942404980777

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/525775.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Godot插值、贝塞尔曲线和Astar寻路

一、插值 线性插值是采用一次多项式上进行的插值计算&#xff0c;任意给定两个值A和B&#xff0c;那么在A和B之间的任意值可以定义为&#xff1a;P(t) A * (1 - t) B * t&#xff0c;0 < t < 1。 数学中用于线性拟合&#xff0c;游戏应用可以做出跟随效果&#xff08;…

keycloak - 鉴权VUE

目录 一、前言 1、背景 2、实验版本 二、开始干活 1、keycloak配置 a、创建领域(realms) b、创建客户端 c、创建用户、角色 2、vue代码 a、依赖 b、main.js 三、未解决的问题 目录 一、前言 1、背景 2、实验版本 二、开始干活 1、keycloak配置 a、创建领域(r…

VMware Esxi安装群辉系统

群晖的网络存储产品具有强大的操作系统&#xff0c;提供了各种应用程序和服务&#xff0c;包括文件共享、数据备份、多媒体管理、远程访问等。用户可以通过简单直观的界面来管理他们的存储设备&#xff0c;并且可以根据自己的需求扩展设备的功能。总的来说&#xff0c;群晖的产…

概念科普|大模型它到底是什么?

一、引言 ChatGPT、Open AI、大模型、提示词工程、Token、幻觉等人工智能的黑话&#xff0c;在2023年这个普通却又神奇的年份里&#xff0c;反复的冲刷着大家的认知。让一部分人彻底躺平的同时&#xff0c;让另外一部分人开始焦虑起来&#xff0c;生怕在这个人工智能的奇迹之年…

鸡乐盒网页版

前端时间鸡乐盒比较火&#xff0c;当时跟着做了一款鸡乐盒&#xff0c;同时拥有聊天以及音乐播放器功能 链接&#xff1a; 鸡乐盒https://www.jaron.top/app/xiana/pages/musicBox/musicBox

C语言---浮点数在内存中的存储

前面跟大家介绍了整数在内存中的存储&#xff0c;这次再向大家介绍下浮点数在内存中的存储。 我们常见的浮点数有3.14&#xff0c;1E10 等等&#xff0c;浮点数家族包括float&#xff0c;double&#xff0c;long double类型。 浮点数的表示范围在头文件 float.h 定义。 1.浮…

代码随想录算法训练营第二十九天(回溯5)|491. 非递减子序列、46. 全排列、47. 全排列 II(JAVA)

文章目录 491. 非递减子序列解题思路源码 46. 全排列解题思路源码 47. 全排列 II解题思路源码 总结 491. 非递减子序列 给你一个整数数组 nums &#xff0c;找出并返回所有该数组中不同的递增子序列&#xff0c;递增子序列中 至少有两个元素 。你可以按 任意顺序 返回答案。 …

探索未来游戏:生成式人工智能AI如何重塑你的游戏世界?

生成式人工智能&#xff08;Generative AI&#xff09;正以前所未有的速度改变着各行各业的运作模式。其中&#xff0c;游戏产业作为科技应用的前沿阵地&#xff0c;正经历着前所未有的变革。本文将探讨生成式人工智能如何重塑游戏产业&#xff0c;以及这一变革背后的深远影响。…

博士推荐 | 拥有超过10年的数据解决方案经验,数据驱动的决策者

编辑 / 木子 审核 / 朝阳 伟骅英才 伟骅英才致力于以大数据、区块链、AI人工智能等前沿技术打造开放的人力资本生态&#xff0c;用科技解决职业领域问题&#xff0c;提升行业数字化服务水平&#xff0c;提供创新型的产业与人才一体化服务的人力资源解决方案和示范平台&#x…

评估精益管理培训的有效性需要收集哪些数据?

近年来&#xff0c;企业纷纷寻求通过精益管理培训来提升效率和竞争力。然而&#xff0c;精益管理培训是否真正有效&#xff0c;能否为企业带来实实在在的改变&#xff0c;这是许多管理者和决策者关心的问题。为了回答这个问题&#xff0c;我们需要收集一系列关键数据来评估精益…

基于 OpenHarmony PrecentPositionLayout 开发指南

1. PrecentPositionLayout 功能介绍 1.1. 组件介绍&#xff1a; 鸿蒙 SDK 提供了不同布局规范的组件容器&#xff0c;例如以单一方向排列的 DirectionalLayout、以相对位置排列的DependentLayout、以确切位置排列的 PositionLayout 等。 但是 PositionLayout 中组件的位置是…

基于单片机收音机调幅系统设计仿真源码

**单片机设计介绍&#xff0c;基于单片机收音机调幅系统设计仿真源码 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于单片机收音机调幅系统设计的仿真源码&#xff0c;主要实现了通过单片机控制调幅收音机的核心功能。以下是…

C++ | Leetcode C++题解之第16题最接近的三数之和

题目&#xff1a; 题解&#xff1a; class Solution { public:int threeSumClosest(vector<int>& nums, int target) {sort(nums.begin(), nums.end());int n nums.size();int best 1e7;// 根据差值的绝对值来更新答案auto update [&](int cur) {if (abs(cur…

城市郊野公园“风筝节”视频智能识别技术安全监管方案

一、方案背景 四月天气十分舒适&#xff0c;微风拂面&#xff0c;这段时间也是游客前往户外放风筝的好时机&#xff0c;很多城市都举办了“风筝节”等活动&#xff0c;尤其是在周末节假日期间&#xff0c;城市各个郊野公园的游客量逐渐暴增。然而&#xff0c;随着参与人数的增…

C++ 十进制转十六进制

文章目录 toHexString(int n) 函数&#xff0c;输入整型数字n&#xff0c;返回 字符串string类。 #include <iostream> #include <string> #include <vector> #include <math.h> using namespace std;string toHexString(int n) {vector <int> …

CICD流水线 发布应用到docker镜像仓库

准备工作 1.先注册免费的镜像仓库 复制链接: https://cr.console.aliyun.com/cn-beijing/instances 实施 1. 新建流水线&#xff0c;选择模板 2.添加流水线源&#xff0c;及是你的代码仓库, 选择对应分支. 3.代码检查以及单元测试&#xff0c;这个步骤可以不用动它. 4. …

Java学习笔记23(面向对象三大特征)

1.5 多态 ​ *多态(polymorphic) ​ *方法或对象具有多种形态&#xff0c;是面向对象的第三大特征&#xff0c;多态是建立在封装和继承基础之上的 1.多态的具体体现 1.方法的多态 &#xff08;重写和重载体现了多态&#xff09; 2.对象的多态 ​ 1.一个对象的编译类型和…

如何魔改 diffusers 中的 pipelines

如何魔改 diffusers 中的 pipelines 整个 Stable Diffusion 及其 pipeline 长得就很适合 hack 的样子。不管是通过简单地调整采样过程中的一些参数&#xff0c;还是直接魔改 pipeline 内部甚至 UNet 内部的 Attention&#xff0c;都可以实现很多有趣的功能或采样生图结果。 本…

如何编写一份完整的软件测试报告

软件测试是软件开发过程中一个非常重要的环节&#xff0c;它有助于确保软件的质量和稳定性。编写一份完整的软件测试报告是软件测试工作的重要组成部分&#xff0c;它不仅可以帮助测试团队记录测试结果和发现的问题&#xff0c;还可以为开发团队提供有价值的反馈和改进建议。下…

我独自升级崛起下载慢/下载不了/无法下载的解决方法

我独自升级是一款RGP游戏&#xff0c;以充满独特的画风以及新的类型的挑战得到玩家们的喜爱。通过打怪、完成任务、参加活动等方式获得经验值&#xff0c;合理分配技能点数和属性点数&#xff0c;以适应不同阶段的挑战需求。掌握装备掉落、合成、升级机制&#xff0c;及时更换更…