• 新手程序员常见的编码错误


    新手程序员常见的编码错误


    前言

    前言:新手程序员基本上都会犯的错误盘点,很多人刚开始写代码都是迫不及待的项目一到手就开始敲,一定一定一定要想想清楚,再开始动手写代码,一个合格的码农,是一个有思想的码农!而不是上来就敲代码的机器人。


    一、没有了解需求就开始写代码

    作为新手,为了展示自己的能力,刚刚拿到需求,就开始迫不及待地上手写代码,这是大忌!
    有些人匆匆看一眼需求就开始做框架,有些人没看仔细没捋清楚就自以为了解了就开始写,到最后发现跟需求跑偏。更有些莽夫程序员上手就直接敲,只看类型不看需求就开始干。
    建议:怎么说呢,有干劲是好的,但是一定要把项目需求,完完整整的,条理清晰的搞清楚。这样会减少很多很多很多的工作难度

    二、不沟通交流就开始做需求

    有的新手程序员不爱说话,不爱沟通,有的时候需求都理解错误了,结果最后做出来才发现,只能加班返工。其实很简单的一件事情往往都会被忽略,就想去考试你连考的什么科目都不看清楚,上去就答题那又怎么可能考高分呢
    建议:一定要记得在拿到需求的时候,和对方多多进行交流和沟通,这样子才可以很好的理解需求,不会误解,从而少做很多无用功。不懂就问嘛,又不丢人。做事没有计划多办都是在做无用功三:沟通的时候就只是沟通,不懂得记录
    文档的作用,很多时候不是用来沟通的,而是用来做记录的,很多的需求还是通过口头沟通,但是不写文档做记录,后续就容易扯皮。这里要划重点做笔记,有多少程序员在这个地方吃过亏,掉这个坑里的程序员堆起来怕是能绕地球十圈了。
    建议:一定要记得现在沟通的时候做好记录,免得对方在后期反口!

    三、代码素养差

    第三点是大多数新手常遇到的问题,归其一点主要原因就是代码写的少、看得少。优秀的代码看多了写多了可以极大的提升你的代码素养。写代码其实跟写文章一样的,有异曲同工之妙。以下我以本人学习期间常遇到的一些编码问题的总结,分享出来与大家共勉。

    常见的编码问题:

    在这里插入图片描述

    四、编码问题总结

    4.0不注重代码格式

    4.0.1空格

    有时候必要的空格没有加(示例)

    void test1(){
    addLog("test1");
     if (condition1){
     if (condition2){
     if (condition3){
     log.info("info:{}",info);
      }
      }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    正解:

    void test1() {
           addLog("test1");
           if (condition1) {
             if (condition2) {
               if (condition3) {
                   log.info("info:{}", info);
                }
              }
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    4.0.2换行

    改换行时没有换行(示例)

        //更多菜单
        m_menuMore = new QMenu(this);
        m_menuMore->setCursor(Qt::PointingHandCursor);
        m_menuMore->setStyleSheet(QSS_MORE_MENU);aboutAction = new QAction(tr(ABOUT_NAME), m_menuMore);aboutAction->setIcon(QIcon(QString(ABOUT_PATH)));
    
    
    • 1
    • 2
    • 3
    • 4
    • 5

    正解:

        //更多菜单
        m_menuMore = new QMenu(this);
        m_menuMore->setCursor(Qt::PointingHandCursor);
        m_menuMore->setStyleSheet(QSS_MORE_MENU);
        aboutAction = new QAction(tr(ABOUT_NAME), m_menuMore);
        aboutAction->setIcon(QIcon(QString(ABOUT_PATH)));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    4.1随意命名

    代码中虽然没有强制要求参数、方法、类或者包名该怎么起名。但如果我们没有养成良好的起名习惯,随意起名的话,可能会出现很多奇怪的代码。一个好的命名可以让人见名知意

    4.1.1有意义的参数名

    代码如下(示例):

    int a = 1;
    int b = 2;
    String c = "abc";
    boolean b = false;
    
    • 1
    • 2
    • 3
    • 4

    4.1.2见名知意

    int supplierCount = 1;
    int purchaserCount = 2;
    String userName = "abc";
    boolean hasSuccess = false;
    
    • 1
    • 2
    • 3
    • 4

    4.2从不写注释

    有时候,在项目时间比较紧张时,很多人为了快速开发完功能,在写代码时,经常不喜欢写注释。

    此外,还有些技术书中说过:好的代码,不用写注释,因为代码即注释。这也给那些不喜欢写代码注释的人,找了一个合理的理由。
    写注释有助于很快的去理解代码的逻辑跟用意
    比如:

    void GfSso::on_TimerEvent() {
    #ifdef _WIN32
        HWND m_hwndDisplay = (HWND)this->winId();
    
        FLASHWINFO fInfo;
        fInfo.cbSize = sizeof(FLASHWINFO);
        fInfo.hwnd = m_hwndDisplay;  //要闪烁的窗口的句柄,该窗口可以是打开的或最小化的
        fInfo.dwFlags = 3;           //闪烁的类型
        fInfo.uCount = 8;            //闪烁的次数
        fInfo.dwTimeout = 300;       //闪烁的频度,毫秒为单位;
    
        FlashWindowEx(&fInfo);
        // FlashWindow(HWND(this->winId()), true);
    
    #endif
        m_pTimer->stop();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    4.3方法过长

    void GfSso::showGfMsgBox(int msgType, QString text) {
        int width = 0;
        int height = 0;
    
        switch (msgType) {
            case 0: {
                gfMsgBox *msgBox = new gfMsgBox(this, MsgBoxType_Warn, text);
                QRect rect = this->geometry();
                //计算显示原点
                locationX = rect.x() + (this->width() - widgetX) / 2;
                locationY = rect.y() + (this->height() - widgetY) / 2;
    
                msgBox->move(width, height);
                msgBox->exec();
    
            } break;
            case 1: {
                gfMsgBox *msgBox = new gfMsgBox(this, MsgBoxType_Warn, text);
                QRect rect = this->geometry();
                //计算显示原点
                locationX = rect.x() + (this->width() - widgetX) / 2;
                locationY = rect.y() + (this->height() - widgetY) / 2;
                msgBox.exec();
            } break;
            case 2: {
                gfMsgBox *msgBox = new gfMsgBox(this, MsgBoxType_Warn, text);
                QRect rect = this->geometry();
                //计算显示原点
                locationX = rect.x() + (this->width() - widgetX) / 2;
                locationY = rect.y() + (this->height() - widgetY) / 2;
                msgBox.exec();
            } break;
            default:
                break;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    可以改为:

    void GfSso::getCoordinate(int &locationX, int &locationY, int widgetX, int widgetY) {
        QRect rect = this->geometry();
        //计算显示原点
        locationX = rect.x() + (this->width() - widgetX) / 2;
        locationY = rect.y() + (this->height() - widgetY) / 2;
    }
    void GfSso::showGfMsgBox(int msgType, QString text) {
        int width = 0;
        int height = 0;
    
        switch (msgType) {
            case 0: {
                gfMsgBox *msgBox = new gfMsgBox(this, MsgBoxType_Warn, text);
                getCoordinate(width, height, msgBox->width(), msgBox->height());
                msgBox->move(width, height);
                msgBox->exec();
    
            } break;
            case 1: {
                gfMsgBox msgBox(this, MsgBoxType_Right, text);
                getCoordinate(width, height, msgBox->width(), msgBox->height());
                msgBox.exec();
            } break;
            case 2: {
                gfMsgBox msgBox(this, MsgBoxType_Error, text);
                getCoordinate(width, height, msgBox->width(), msgBox->height());
                msgBox.exec();
            } break;
            default:
                break;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    4.4参数过多

    示例:

    void fun(String a,
                  String b,
                  String c,
                  String d,
                  String e,
                  String f) {
       ...
    }
    
    void client() {
       fun("a","b","c","d",null,"f");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    4.5代码层级太深

    示例:

    if (a == 1) {
       if(b == 2) {
          if(c == 3) {
             if(d == 4) {
                if(e == 5) {
                  ...
                }
                ...
             }
             ...
          }
          ...
       }
       ...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    这段代码中有很多层if判断,是不是看得人有点眼花缭乱?
    对于深层级的判断可以先行判断不满足条件的逻辑,先返回。

    4.6硬编码

    这里举个例子:

    mProxy = new pkiAgentProxy("CommonTest", "e3d93edf-d7e4-13da-2eaf-d63724026f34", "56a4d6a2-ef41-8ee7-d3a5-ac3a7ccdb9d7");
    
    • 1

    当这样的使用范围很广时或者需要更改时就会变得很麻烦。可以使用宏的方式定义,这样只需要改变一处的宏就可以一劳永逸了。

    #define APP_NAME "CommonTest";
    #define APP_ID "e3d93edf-d7e4-13da-2eaf-d63724026f34";
    #define APP_TOKEN "56a4d6a2-ef41-8ee7-d3a5-ac3a7ccdb9d7";
    
    mProxy = new pkiAgentProxy(APP_NAME ,APP_ID ,APP_TOKEN );
    
    • 1
    • 2
    • 3
    • 4
    • 5

    4.7不正确的日志打印

    在我们写代码的时候,打印日志是必不可少的工作之一。
    因为日志可以帮我们快速定位问题,判断代码当时真正的执行逻辑。
    但打印日志的时候也需要注意,不是说任何时候都要打印日志,比如:

    	map<int, int>::iterator ret = m.find(1);
    	if (ret != m.end())
    	{
    		cout << "查找到数据元素为:" << ret->first<<" "<< ret->second << endl;
    		TRUSTCONTROL_DEBUG("找到数据元素!");
    	}
    	else
    	{
    		cout << "元素未找到" << endl;
    		TRUSTCONTROL_DEBUG("未找到数据元素!");
    	}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    对于有些查询接口,在日志中打印出了请求参数和接口返回值。

    咋一看没啥问题。但如果传入值非常多,比如有1000个。而该接口被调用的频次又很高,一下子就会打印大量的日志,用不了多久就可能把磁盘空间打满。

    对于日志我们只需要打印需要的或者错误的地方就可以了

    4.8没校验入参

    void parseStr(constr char *str){
        std::string = str;
    
    }
    
    • 1
    • 2
    • 3
    • 4

    乍一看没什么问题,但是如果传进来的str是NULL时就会报错了。所以参数为指针时记得要做入参判断

    4.9返回值格式不统一

    对接接口返回值结构不一致:

    {
       "ret":0,
       "message":null,
       "data":[]
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    另一边:

    {
       "code":0,
       "msg":null,
       "success":true,
       "result":[]
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    4.10提交到Git的代码不能编译通过

    我们写完代码之后,把代码提交到gitlab上,也有一些讲究。
    最最忌讳的是代码还没有写完,因为粗心大意导致代码只上传了一部分,例如:

    void test() {
       String userName="苏三";
       String password=
    }
    
    • 1
    • 2
    • 3
    • 4

    这段代码中的password变量都没有定义好,项目一运行起来必定报错。
    这种错误的代码提交方式,一般是新手会犯。但还有另一种情况,就是在多个分支merge代码的时候,有时候会出问题,merge之后的代码不能正常运行,就被提交了。

    好的习惯是:用git提交代码之前,一定要在本地运行一下,确保项目能正常启动才能提交。宁可不提交代码到远程仓库,切勿因为一时赶时间,提交了不完整的代码,导致团队的队友们项目都启动不了。

    4.11不处理没用的代码

    有时候我们在代码开发测试中添加了很多调试手段,部分代码已经不需要用到了但是我们任然保留着。
    例如:

        std::string message = buildSsoMsg(mLabel, authType, loginType, userName, passWord);
        emit sendData(QString::fromStdString(message));
    
        // 发送登录消息后,设置登录按钮不使能,防止多次点击
        // setLoginBtnEnable(false);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    4.12从不写单元测试

    因为项目时间实在太紧了,系统功能都开发不完,更何况是单元测试呢?
    大部分人不写单元测试的原因,可能也是这个吧。

    那么,我们为什么要写单元测试呢?

    • 我们写的代码大多数是可维护的代码,很有可能在未来的某一天需要被重构。试想一下,如果有些业务逻辑非常复杂,你敢轻易重构不?如果有单元测试就不一样了,每次重构完,跑一次单元测试,就知道新写的代码有没有问题。

    • 我们新写的对外接口,测试同学不可能完全知道逻辑,只有开发自己最清楚。不像页面功能,可以在页面上操作。他们在测试接口时,很有可能覆盖不到位,很多bug测不出来。

    建议由于项目时间非常紧张,在开发时确实没有写单元测试,但在项目后期的空闲时间也建议补上。

    4.13 魔数和字符串

    4.13.1魔数

    for (int i = 1; i <= 52; i++) {
    
    ...
    }
    
    • 1
    • 2
    • 3
    • 4

    i为什么是1?53?看不懂…

    4.13.2字符串

    if (userPasswordIsValid($user,"6yP4cZ",password)) {
    ...
    6yP4cZ是什么?似乎非常随意...
    }
    
    • 1
    • 2
    • 3
    • 4

    4.14释放指针未判断

    4.14.1释放之前未判断

    {
        json_value_free(root_value);
        return bRet;
    }
    
    • 1
    • 2
    • 3
    • 4

    4.14.2释放之后未置空

    改为:

    {
        if(root_value){
            json_value_free(root_value);
            root_value = NULL;
        }
        return bRet;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    4.15出现大量重复代码

    void test1()  {
            addLog("test1");
        }
        void addLog(String info) {
            if (log.isInfoEnabled()) {
                log.info("info:{}", info);
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    void test2()  {
            addLog("test2");
        }
        void addLog(String info) {
            if (log.isInfoEnabled()) {
                log.info("info:{}", info);
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    void test3()  {
            addLog("test3");
        }
        void addLog(String info) {
            if (log.isInfoEnabled()) {
                log.info("info:{}", info);
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    三个函数中均使用了addLog(),为何不把这种功能的代码提取出来,放到某个工具类中呢?

    4.16源数据未使用const

    当我们不想改变源数据时,要使用coonst修饰

    例如:

    static std::string buildMsg(const std::string &Str, int authType) {
    ...
    }
    
    • 1
    • 2
    • 3
    void test2()  {
            addLog("test2");
        }
        void addLog(String info) {
            if (log.isInfoEnabled()) {
                log.info("info:{}", info);
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    4.17代码中过多的ifelse判断

      if (status == 1) {
        // 逻辑1
      } else if (status == 2) {
        // 逻辑2
      } else if (status == 3) {
        // 逻辑3
      } else if (status == 4) {
        // 逻辑4
      } else if (status == 5) {
        // 逻辑5
      } else {
        // 逻辑6
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    这段代码有什么问题呢?试想一下,如果需要判断的条件越来越多,比如:又加了新的判断逻辑、增加新的else…if判断,判断多了就会导致逻辑越来越多?

    很明显,这里违法了设计模式六大原则的:开闭原则 和 单一职责原则。

    开闭原则:对扩展开放,对修改关闭。就是说增加新功能要尽量少改动已有代码。

    单一职责原则:顾名思义,要求逻辑尽量单一,不要太复杂,便于复用。

    那么,如何优化if…else判断呢?

    4.17.1switch优化

    switch (status) {
      case 1:
        // 逻辑1
        break
      case 2:
      case 3:
        // 逻辑3、2 两个相同的可以写一起
        break
      case 4:
        // 逻辑4
        break
      case 5:
       // 逻辑5
        break
      default:
        // 逻辑6
        break
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    4.17.2策略模式(多态)

    #include 
    #include 
    #include 
    using namespace std;
    class WeaponStrategy
    {
    public:
    	virtual void UseWeapon() = 0;
    
    };
    
    class Knife:public WeaponStrategy
    {
    public:
    	virtual void UseWeapon()
    	{
    		cout<<"使用匕首!"<<endl;
    	}
    };
    class AK47:public WeaponStrategy
    {
    public:
    	virtual void UseWeapon()
    	{
    		cout<<"使用AK47"<<endl;
    	}
    };
    
    class Charcter
    {
    public:
    	void setWeapon(WeaponStrategy* weapon)
    	{
    		this->pWeapon = weapon;
    	}
    	void ThrowWeapon()
    	{
    		this->pWeapon->UseWeapon();
    	}
    	WeaponStrategy* pWeapon;
    };
    int main()
    {
    	{
    		//创建角色
    		Charcter* t_charater = new Charcter;
    		//武器策略
    		WeaponStrategy* t_knife = new Knife;
    		WeaponStrategy* t_ak47 = new AK47;
    
    		t_charater->setWeapon(t_knife);
    		t_charater->ThrowWeapon();
    		t_charater->setWeapon(t_ak47);
    		t_charater->ThrowWeapon();
    
    		delete t_knife;
    		delete t_ak47;
    		delete t_charater;
    		t_ak47 = nullptr;
    		t_charater = nullptr;
    		t_knife = nullptr;
    		
    	}
    	system("pause");
    	return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67

    总结

    细节决定成败

    所以基础很重要,俗话说:千里之行始于足下!

  • 相关阅读:
    Sound/播放提示音, Haptics/触觉反馈, LocalNotification/本地通知 的使用
    Python中pip在cmd命令行下无法使用的解决方案
    单商户商城系统功能拆解30—营销中心—积分签到
    Android 开机流程介绍
    Python 教程之输入输出(1)—— 在 Python 中接受输入
    MIPI CSI-2笔记(24) -- Sleep Mode
    FPGA - 7系列 FPGA SelectIO -09- 高级逻辑资源之IO_FIFO
    基于springboot实现汽车租赁管理系统项目演示【项目源码+论文说明】分享
    python代码规范PEP 8——常见的规范错误与解决办法
    聊聊数据库连接池 Druid
  • 原文地址:https://blog.csdn.net/qq_58397358/article/details/126394066