其他
初级程序员常犯错误一览
The following article is from 码农桃花源 Author Xargin
1
命名太啰嗦,或者不规范
这个问题其实挺普遍的,即使是科班出身的人,工作了很多年的人,写程序也可能冒出来一堆var a/b/c之类的变量。如果你的程序本身短小精悍,或者只是纯粹的算法题,这样似乎问题不大(反正oj ac了以后你可能也就再也不会看自己的代码。但这样的代码放在生产环境中,问题就很大了。首先有些变量本身的生命周期可能会超过一个横着放的屏幕。这里对说一句,不是谁都能买得起Dell的可横可竖的屏幕好吗。比如按照我的13寸笔记本来看,vim的全屏模式下只能显示33行(当然了,我视力不好,字体比较大)。所以如果在第一行声明了一个变量var a。那么顺着读下去,在33行之外......我觉得我肯定不记得这是什么东西了。
KnowledgeService kService;
OrderService oService;
UserMapper uMapper;
2
魔法数字
这个问题在哪里都看得到,最简单的例如各种订单的status跳转。你会发现各种updateStatus(1)之类的神奇代码。如果恰巧数据库的表定义里又没有这个status的定义,那必然会变成维护人员的噩梦。解决方法很简单,在配置文件中集中维护这些特殊变量。
3
解决了魔法数字的问题,但不对配置文件进行分类
有了配置系统以后,其实还有个麻烦的问题。就是所有配置都放在一个文件里。例如之前做的系统,把所有key和对应的sql都放在一起,然后导致配置文件变得很大很难看。想改个简单的sql连找都找不到在哪里。
4
分类粒度问题
对配置进行了分类,但有时候也有可能配置粒度实在太细,导致文件很多,找起来还是很不方便。
5
函数写的太长
这也是常见的问题。业务开发在堆代码的阶段特别容易引起这种问题。例如上一家公司的createOrder逻辑,一开始只是80行的小函数。但后来陆续加入了商品内容校验,用户对商品的合法性校验(主要是指用户是否是跨越了购买区域啊,或者购买了本没有资格购买的vip商品),价格校验(是否优惠,优惠了多少,前端的价格有没有问题之类的)等等。然后就让事情变得越来越难以收拾,80行的开头最终变成了1000行的巨无霸。
func ABCDE() {
A
B
C
D
E
}
=>
func main() {
A()
B()
C()
D()
E()
}
func createOrder(userInfo UserInfo, products []Product) {
checkUserValid(userInfo);
checkProducts(products);
checkUserAndProducts(userInfo, products);
var order = insertOrder(userInfo, products);
var createFlag = createOrderDetail(order, products);
return createFlag;
}
6
滥用回调,增加系统复杂性
滥用回调其实挺常见的,特别是在现在这个公司。很多时候有数据依赖的项目会彼此之间搞一堆错综复杂的回调接口。这样在初期开发的时候因为调用是同步操作,所以看起来项目很简单,而且同步调用随时打日志,出了问题也容易排查。
这里有一个用户的评论系统,评论系统会对服务你的商家进行一些tag勾选和内容填空。
对于评论系统本身,只需要简单记录被打tag和被评论的对象到mysql即可。
但后面有信用系统、用户画像系统、客服系统依赖于这些评论的数据,所以需要把这些评论tag和内容同步给其它的几个系统,或者甚至是跨部门的系统。
event A happend
then {
call sys A1();
call sys A2();
call sys A3();
call sys A4();
call sys A5();
call sys A6();
call sys A7();
call sys A8();
...
}
=>
event A happend
then {
push msg to msg queue
}
A1~AN subscribe topic A in msg queue
7
虽然有分层,但每层的结果返回没有明确界线和定义
这个在啥语言的项目里都比较多。
8
明明提供的是公共接口,但是其本身却只能使用一次
这个错误。其实很简单,就是常说的可重入不可重入的概念,当年刚毕业的时候我也被很多人问到,但那个时候确实没法理解。不过其实很简单,如果你的函数在被调用时不能保证返回结果的正确性,例如使用了全局变量且没有加锁,或者使用了静态局部变量,那么这个函数就是不可重入的。但如果你的环境是php的话,这里又有一些微妙的不同。因为php本身是单线程运行,所以所谓的不可重入就只是你丫别用全局变量来存储函数计算的结果啊。。。
在这个部门的XX系统里有这么一段php代码:
global $validUserList = array();
public static getValidUserList($users) {
global $validUserList;
for($users as $user) {
$validUserList[] = $user;
}
}
public static getValidUserList($users) {
$validUserList = array();
for($users as $user) {
$validUserList[] = $user;
}
return $validUserList;
}
9
设计模式滥用
这一点可能是不太好界定的一点,用设计模式本身就是为了把重复的工作进行一次性化。但问题很多时候不在于你用了什么设计模式,而在于写这段代码的人是谁。比如有人用的明明是策略模式,但你在他的代码的字里行间都看不出来这是策略模式,只看到什么getOm(其实是莫名其妙的getObjectModel的缩写),再通过反射去找到一个写死了名字的xxxxfunction,再直接通过字符串进行函数调用(php才可以这样),读得人云里雾里。而且待你把他的代码全部扫过一遍之后才发现,虽然用了策略模式,但这段代码只有一种策略,其功能只是把数据库里的一个表的一个字段修改为一个固定的状态值。
10
访问数据库不做批量
比较典型的场景,现在大多数的web程序都可以分为列表页和详情页。。。说批量,其实主要说的就是列表页的问题。
func getProductList(xxxx) {
var products []Product
for product := range products {
categoryName := getCategoryName(product.getCategoryId())
product.setCategoryName()
}
}
11
树形结构的表结构设计问题
公司里有人会把存储树形结构的表设计成这样:
Field | Type | Null | Key | Default | Extra |
+------------------+--------------+------+-----+---------------------+-----------------------------+
| id | int(10) | NO | PRI | NULL | auto_increment |
| name | varchar(64) | NO | | | |
| parent_id | int(10) | NO | | 0 | |
| status | tinyint(3) | NO | | 0 | |
| createtime | datetime | NO | | 0000-00-00 00:00:00 | |
| modifytime | timestamp | NO | | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP |
| category_id_path | varchar(256) | NO | | 0 | |
id: 3459
name: fasdfasf
parent_id: 0
status: 0
createtime: 2016-10-20 17:11:12
modifytime: 2016-10-20 17:11:13
category_id_path: 1,345,2359
1 row in set (0.01 sec)
select * from category where category_id_path like '1,345%'
select * from category where category_id_path like '1,345,'
select * from category where category_id_path like '1,345,' or category_id_path = '1,345'
12
if/else嵌套层次
从例子开始吧:
func createUser(user UserInfo) {
if user.Age <= 0 {
inValid = true;
} else {
if user.Name == "" {
inValid = true
} else {
if user.History.Length == 0 {
} else {
//500行
//500行
//500行
//500行
//500行
//500行
//500行
}
}
}
return inValid;
}
func createUser(user UserInfo) {
if user.Age <= 0 {
inValid = true;
return inValid
}
if user.Name == "" {
inValid = true
return inValid
}
if user.History.Length == 0 {
inValid = true;
return inValid;
}
//500行
return inValid;
}
13
同一张数据库表的查询,每换一种查询方式就写一个函数
还是我们的线上系统,dao层有这么个mapper:
public interface CategoryMapper xxxx {
@Select("select * from category where name = #{name}")
public List<Category> findByName(@Param("name") String name);
@Select("select * from category where id = #{id}")
public List<Category> findById(@Param("id") Long id);
@Select("select * from category where parentId = #{parentId}")
public List<Category> findByParentId(@Param("parentId") Long parentId);
@Select("select * from category where status = #{status}")
public List<Category> findByStatus(@Param("status") Integer status);
//以下略
}
14
工作流系统update不判断修改前的状态
工作流/订单状态流之类的系统都会有状态流转,其实和编译原理里的状态机意思差不多。不同的状态之间跳转应该有一个基本的先置条件,从某一个固定的状态,满足某一些条件,然后跳转到下一个状态中。但实际的业务系统,却让人发现很多系统在做状态变更的时候,根本不考虑前置状态。比如下面的代码:
update xxx set status = yyy where id = zzz;
15
非单线程系统不考虑线程安全问题
这个问题其实说起来挺复杂的。。多线程程序里很难查的大多是这种问题,所以现在一般做非性能要求很高的系统都会尽量避免掉多线程并发。或者在该并发的时候再并发执行特定的任务,比如java里用future或者ExecutorService之类的来进行数据库并发查询,以减少串行执行任务的等待时间。
16
open的资源不关闭,造成句柄泄露
这个错常由php转其它语言的程序员来犯。我们php程序员open的东西从来不close。
17
抱怨接口性能是语言问题
之前有这么一个程序员,在做了一个要3s才要返回的接口之后说这是贵司php版本5.3的原因,你要是让我换php 7肯定能快十倍。
- END -
文章精选1、Flash将停止支持!分享一个让你能继续使用Flash的神器2、一次出人意料而名留青史的 DNS 投毒攻击3、细思极恐!居然在简历中藏木马!4、360安全浏览器从“永久免费”变“VIP收费”?官方回应了5、程序员用 M1 MacBook 当开发主力机是什么体验?
6、C++自学效率太低,该放弃吗?
7、我的电脑不联网,很安全,黑客:你还有风扇呢8、世界上最好玩的6种表情符号编程语言
6、C++自学效率太低,该放弃吗?
7、我的电脑不联网,很安全,黑客:你还有风扇呢8、世界上最好玩的6种表情符号编程语言