新手程序员常见的编码错误
文章目录
前言
前言:新手程序员基本上都会犯的错误盘点,很多人刚开始写代码都是迫不及待的项目一到手就开始敲,一定一定一定要想想清楚,再开始动手写代码,一个合格的码农,是一个有思想的码农!而不是上来就敲代码的机器人。
一、没有了解需求就开始写代码
作为新手,为了展示自己的能力,刚刚拿到需求,就开始迫不及待地上手写代码,这是大忌!
有些人匆匆看一眼需求就开始做框架,有些人没看仔细没捋清楚就自以为了解了就开始写,到最后发现跟需求跑偏。更有些莽夫程序员上手就直接敲,只看类型不看需求就开始干。
建议:怎么说呢,有干劲是好的,但是一定要把项目需求,完完整整的,条理清晰的搞清楚。这样会减少很多很多很多的工作难度
二、不沟通交流就开始做需求
有的新手程序员不爱说话,不爱沟通,有的时候需求都理解错误了,结果最后做出来才发现,只能加班返工。其实很简单的一件事情往往都会被忽略,就想去考试你连考的什么科目都不看清楚,上去就答题那又怎么可能考高分呢
建议:一定要记得在拿到需求的时候,和对方多多进行交流和沟通,这样子才可以很好的理解需求,不会误解,从而少做很多无用功。不懂就问嘛,又不丢人。做事没有计划多办都是在做无用功三:沟通的时候就只是沟通,不懂得记录
文档的作用,很多时候不是用来沟通的,而是用来做记录的,很多的需求还是通过口头沟通,但是不写文档做记录,后续就容易扯皮。这里要划重点做笔记,有多少程序员在这个地方吃过亏,掉这个坑里的程序员堆起来怕是能绕地球十圈了。
建议:一定要记得现在沟通的时候做好记录,免得对方在后期反口!
三、代码素养差
第三点是大多数新手常遇到的问题,归其一点主要原因就是代码写的少、看得少。优秀的代码看多了写多了可以极大的提升你的代码素养。写代码其实跟写文章一样的,有异曲同工之妙。以下我以本人学习期间常遇到的一些编码问题的总结,分享出来与大家共勉。
常见的编码问题:
四、编码问题总结
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;
}
总结
细节决定成败
所以基础很重要,俗话说:千里之行始于足下!