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

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


前言

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


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

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

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

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

三、代码素养差

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

常见的编码问题:

在这里插入图片描述

四、编码问题总结

4.0不注重代码格式

4.0.1空格

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

void test1(){
addLog("test1");
 if (condition1){
 if (condition2){
 if (condition3){
 log.info("info:{}",info);
  }
  }
  }
}

正解:

void test1() {
       addLog("test1");
       if (condition1) {
         if (condition2) {
           if (condition3) {
               log.info("info:{}", info);
            }
          }
        }
    }

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)));

正解:

    //更多菜单
    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)));

4.1随意命名

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

4.1.1有意义的参数名

代码如下(示例):

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

4.1.2见名知意

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

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();
}

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;
    }
}

可以改为:

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;
    }
}

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");
}

4.5代码层级太深

示例:

if (a == 1) {
   if(b == 2) {
      if(c == 3) {
         if(d == 4) {
            if(e == 5) {
              ...
            }
            ...
         }
         ...
      }
      ...
   }
   ...
}

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

4.6硬编码

这里举个例子:

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

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

#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 );

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("未找到数据元素!");
	}
}

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

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

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

4.8没校验入参

void parseStr(constr char *str){
    std::string = str;

}

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

4.9返回值格式不统一

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

{
   "ret":0,
   "message":null,
   "data":[]
}

另一边:

{
   "code":0,
   "msg":null,
   "success":true,
   "result":[]
}

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

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

void test() {
   String userName="苏三";
   String password=
}

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

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

4.11不处理没用的代码

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

    std::string message = buildSsoMsg(mLabel, authType, loginType, userName, passWord);
    emit sendData(QString::fromStdString(message));

    // 发送登录消息后,设置登录按钮不使能,防止多次点击
    // setLoginBtnEnable(false);
}

4.12从不写单元测试

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

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

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

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

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

4.13 魔数和字符串

4.13.1魔数

for (int i = 1; i <= 52; i++) {

...
}

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

4.13.2字符串

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

4.14释放指针未判断

4.14.1释放之前未判断

{
    json_value_free(root_value);
    return bRet;
}

4.14.2释放之后未置空

改为:

{
    if(root_value){
        json_value_free(root_value);
        root_value = NULL;
    }
    return bRet;
}

4.15出现大量重复代码

void test1()  {
        addLog("test1");
    }
    void addLog(String info) {
        if (log.isInfoEnabled()) {
            log.info("info:{}", info);
        }
    }
void test2()  {
        addLog("test2");
    }
    void addLog(String info) {
        if (log.isInfoEnabled()) {
            log.info("info:{}", info);
        }
    }
void test3()  {
        addLog("test3");
    }
    void addLog(String info) {
        if (log.isInfoEnabled()) {
            log.info("info:{}", info);
        }
    }

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

4.16源数据未使用const

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

例如:

static std::string buildMsg(const std::string &Str, int authType) {
...
}
void test2()  {
        addLog("test2");
    }
    void addLog(String info) {
        if (log.isInfoEnabled()) {
            log.info("info:{}", info);
        }
    }

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
  }

这段代码有什么问题呢?试想一下,如果需要判断的条件越来越多,比如:又加了新的判断逻辑、增加新的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
}

4.17.2策略模式(多态)

#include <iostream>
#include <vector>
#include <algorithm>
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;
}

总结

细节决定成败

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

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值